hobo 1.1.0.pre2 → 1.1.0.pre3

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.
data/CHANGES.txt CHANGED
@@ -14,6 +14,40 @@ likely to cause conflicts, so it is highly recommended that you have
14
14
  your code backed up and in a change control system such as git or
15
15
  subversion.
16
16
 
17
+ === Hobo 1.1 ===
18
+
19
+ The default password validation has been changed to 6 characters, one
20
+ of which must not be lowercase. Luckily, we also made the password
21
+ validation easier to change. See
22
+ [Bug #638](https://hobo.lighthouseapp.com/projects/8324/tickets/638) for
23
+ more information.
24
+
25
+ === Hobo 1.0.3 ===
26
+
27
+ This is a security release. All applications that use the reset
28
+ password functionality should upgrade.
29
+
30
+ To patch the vulnerability, two changes have been made.
31
+
32
+ First of all, the lifecycle key hash mechanism has been changed.
33
+ Existing lifecycle keys will become invalid after you upgrade.
34
+ Lifecycle keys are typically short lived, so this is unlikely to be a
35
+ problem for most applications.
36
+
37
+ Secondly, lifecycle keys are now cleared on every transition to avoid
38
+ replay vulnerabilities. This new behaviour may be avoided by added
39
+ the `:keep_key => true` option to a transition.
40
+
41
+ More information about the vulnerability can be viewed on the [bug
42
+ report](https://hobo.lighthouseapp.com/projects/8324/tickets/666-user-model-secure-links-have-low-security).
43
+
44
+ All code changes may viewed on the [github
45
+ log](https://github.com/tablatom/hobo/compare/v1.0.2...v1.0.3)
46
+
47
+ The "include" automatic scope has been aliased to "includes" to
48
+ increase future compatibility with Rails 3. Future versions of Hobo
49
+ will remove support for "include".
50
+
17
51
  === Hobo 1.0.2 ===
18
52
 
19
53
  This release is almost identical to 1.0.1 except that it updates the
@@ -95,6 +95,7 @@ Let's set up a few models for our testing:
95
95
  born_at :date
96
96
  code :integer
97
97
  male :boolean
98
+ foo :string
98
99
  timestamps
99
100
  end
100
101
 
@@ -119,19 +120,19 @@ Generate a migration and run it:
119
120
 
120
121
  >> ActiveRecord::Migration.class_eval(HoboFields::MigrationGenerator.run[0])
121
122
  >> Person.columns.*.name
122
- => ["id", "name", "born_at", "code", "male", "created_at", "updated_at", "state"]
123
+ => ["id", "name", "born_at", "code", "male", "foo", "created_at", "updated_at", "state"]
123
124
  {.hidden}
124
125
 
125
126
  And create a couple of fixtures:
126
127
 
127
128
  >>
128
129
  Bryan = Person.new(:name => "Bryan", :code => 17,
129
- :born_at => Date.new(1973,4,8), :male => true)
130
+ :born_at => Date.new(1973,4,8), :male => true, :foo => "common")
130
131
  >> Bryan.state = "active"
131
132
  >> Bryan.save!
132
133
  >>
133
134
  Bethany = Person.new(:name => "Bethany", :code => 42,
134
- :born_at => Date.new(1975,5,13), :male => false)
135
+ :born_at => Date.new(1975,5,13), :male => false, :foo => "common")
135
136
  >> Bethany.state = "inactive"
136
137
  >> Bethany.save!
137
138
  >> Friendship.new(:person => Bryan, :friend => Bethany).save!
@@ -287,12 +288,16 @@ Gives the N most recent items:
287
288
  >> Person.order_by(:code).*.name
288
289
  => ["Bryan", "Bethany"]
289
290
 
290
- ## include
291
+ ## includes
291
292
 
292
- Adding the include function to your query chain has the same effect as
293
+ Adding the includes function to your query chain has the same effect as
293
294
  the `:include` option to the `find` method.
294
295
 
295
- >> Person.include(:friends).*.name
296
+ >> Person.search("B", :name).include(:friends).*.name # test LH#839
297
+ => ["Bryan", "Bethany"]
298
+ .hidden
299
+
300
+ >> Person.includes(:friends).*.name
296
301
  => ["Bryan", "Bethany"]
297
302
 
298
303
  ## search
@@ -397,6 +402,15 @@ Like named scopes, Hobo scopes can be chained:
397
402
  >> Bryan.inactive_friends.inactive.*.name
398
403
  => ["Bethany"]
399
404
 
405
+ >> Person.scoped(:conditions => {:state => "active"}).scoped(:conditions => {:foo => "common"}).*.name
406
+ => ["Bryan"]
407
+
408
+ >> Person.with_friendship(Friendship.first).born_before(Date.new(1974)).*.name
409
+ => ["Bryan"]
410
+
411
+ >> Person.born_after(Date.new(1974)).with_friendship(Friendship.first).*.name
412
+ => []
413
+
400
414
  Clean up our test database:
401
415
  {.hidden}
402
416
 
@@ -25,17 +25,6 @@ module ActiveRecord
25
25
  record
26
26
  end
27
27
 
28
- # DO NOT call super here - AssociationProxy's version loads the collection, and that's bad.
29
- # TODO: this really belongs in Rails; migrate it there ASAP
30
- def respond_to?(*args)
31
- proxy_respond_to?(*args) || Array.new.respond_to?(*args)
32
- end
33
-
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
- def is_a?(klass)
36
- [].is_a?(klass)
37
- end
38
-
39
28
  def member_class
40
29
  proxy_reflection.klass
41
30
  end
@@ -67,7 +67,7 @@ module Hobo
67
67
  accepts.xml do
68
68
  headers["Status"] = "Unauthorized"
69
69
  headers["WWW-Authenticate"] = %(Basic realm="Web Password")
70
- render :text => ht("hobo.messages.unauthenticated", :default=>["Couldn't authenticate you"], :status => '401 Unauthorized')
70
+ render :text => ht("hobo.messages.unauthenticated", :default=>["Couldn't authenticate you"]), :status => '401 Unauthorized'
71
71
  end
72
72
  end
73
73
  false
@@ -58,7 +58,7 @@ module Hobo
58
58
 
59
59
  def redirect_to_with_object_url(destination, *args)
60
60
  if destination.is_one_of?(String, Hash, Symbol)
61
- redirect_to_without_object_url(destination)
61
+ redirect_to_without_object_url(destination, *args)
62
62
  else
63
63
  redirect_to_without_object_url(object_url(destination, *args))
64
64
  end
@@ -78,12 +78,12 @@ module Hobo
78
78
  end
79
79
 
80
80
 
81
- def ajax_update_response(page_path, render_specs, results={})
81
+ def ajax_update_response(page_path, render_specs, results={}, options={})
82
82
  @template.send(:_evaluate_assigns_and_ivars)
83
83
  renderer = Dryml.page_renderer(@template, [], page_path) if page_path
84
84
 
85
85
  render :update do |page|
86
- page << "var _update = typeof Hobo == 'undefined' ? Element.update : Hobo.updateElement;"
86
+ page << (options[:preamble] || "var _update = typeof Hobo == 'undefined' ? Element.update : Hobo.updateElement;")
87
87
  for spec in render_specs
88
88
  function = spec[:function] || "_update"
89
89
  dom_id = spec[:id]
@@ -99,6 +99,7 @@ module Hobo
99
99
  end
100
100
  end
101
101
  page << renderer.part_contexts_storage if renderer
102
+ page << options[:postamble] if options[:postamble]
102
103
  end
103
104
  end
104
105
 
@@ -168,7 +168,6 @@ module Hobo
168
168
  def become(state_name, validate=true)
169
169
  state_name = state_name.to_sym
170
170
  record.write_attribute self.class.state_field, state_name.to_s
171
-
172
171
  if state_name == :destroy
173
172
  record.destroy
174
173
  true
@@ -210,7 +209,7 @@ module Hobo
210
209
  timestamp = record.read_attribute(key_timestamp_field)
211
210
  if timestamp
212
211
  timestamp = timestamp.getutc
213
- Digest::SHA1.hexdigest("#{record.id}-#{state_name}-#{timestamp}")
212
+ Digest::SHA1.hexdigest("#{record.id}-#{state_name}-#{timestamp}-#{ActionController::Base.session_options[:secret]}")
214
213
  end
215
214
  end
216
215
 
@@ -223,6 +222,10 @@ module Hobo
223
222
  provided_key && provided_key == key && !key_expired?
224
223
  end
225
224
 
225
+ def clear_key
226
+ record.write_attribute key_timestamp_field, nil
227
+ end
228
+
226
229
  def invariants_satisfied?
227
230
  self.class.invariants.all? { |i| record.instance_eval(&i) }
228
231
  end
@@ -34,7 +34,8 @@ module Hobo
34
34
 
35
35
 
36
36
  def change_state(record)
37
- record.lifecycle.become(get_state(record, end_state))
37
+ record.lifecycle.clear_key unless options[:new_key] || options[:keep_key]
38
+ return record.lifecycle.become(get_state(record, end_state))
38
39
  end
39
40
 
40
41
 
data/lib/hobo/model.rb CHANGED
@@ -32,13 +32,17 @@ module Hobo
32
32
 
33
33
  alias_method_chain :has_one, :new_method
34
34
 
35
- def inherited(klass)
36
- super
37
- fields(false) do
38
- Hobo.register_model(klass)
39
- field(klass.inheritance_column, :string)
35
+ # eval avoids the ruby 1.9.2 "super from singleton method ..." error
36
+ # see LH#840
37
+ eval %(
38
+ def inherited(klass)
39
+ super
40
+ fields(false) do
41
+ Hobo.register_model(klass)
42
+ field(klass.inheritance_column, :string)
43
+ end
40
44
  end
41
- end
45
+ )
42
46
  end
43
47
 
44
48
  # https://hobo.lighthouseapp.com/projects/8324/tickets/762-hobo_model-outside-a-full-rails-env-can-lead-to-stack-level-too-deep
@@ -121,8 +125,12 @@ module Hobo
121
125
 
122
126
  ActiveRecord::Base.class_eval do
123
127
  def self.hobo_model
124
- include Hobo::Model
125
- fields(false) # force hobofields to load
128
+ if self.ancestors.include?(Hobo::Model)
129
+ Rails.logger.error "#{self}: Do not call hobo_model in derived classes."
130
+ else
131
+ include Hobo::Model
132
+ fields(false) # force hobofields to load
133
+ end
126
134
  end
127
135
  def self.hobo_user_model
128
136
  include Hobo::Model
@@ -455,6 +455,7 @@ module Hobo
455
455
 
456
456
  if do_pagination
457
457
  options.reverse_merge!(:page => params[:page] || 1)
458
+ options[:order] = :created_at if options[:order] == :default
458
459
  finder.paginate(options)
459
460
  else
460
461
  finder.all(options.except(*WILL_PAGINATE_OPTIONS))
@@ -521,7 +522,7 @@ module Hobo
521
522
  this.user_update_attributes(current_user, attributes)
522
523
  else
523
524
  self.this = new_for_create(attributes)
524
- this.save
525
+ this.user_save(current_user)
525
526
  end
526
527
  create_response(:new, options, &b)
527
528
  end
@@ -590,10 +591,11 @@ module Hobo
590
591
 
591
592
  self.this ||= args.first || find_instance
592
593
  changes = options[:attributes] || attribute_parameters or raise RuntimeError, ht(:"hobo.messages.update.no_attribute_error", :default=>["No update specified in params"])
593
- this.user_update_attributes(current_user, changes)
594
-
595
- # Ensure current_user isn't out of date
596
- @current_user = @this if @this == current_user
594
+
595
+ if this.user_update_attributes(current_user, changes)
596
+ # Ensure current_user isn't out of date
597
+ @current_user = @this if @this == current_user
598
+ end
597
599
 
598
600
  in_place_edit_field = changes.keys.first if changes.size == 1 && params[:render]
599
601
  update_response(in_place_edit_field, options, &b)
@@ -267,7 +267,30 @@ module Hobo
267
267
  field, asc = args
268
268
  type = klass.attr_type(field)
269
269
  if type.nil? #a virtual attribute from an SQL alias, e.g., 'total' from 'COUNT(*) AS total'
270
- colspec = "#{field}" # don't prepend the table name
270
+ # can also be has_many association, let's check it out
271
+ _, assoc, count = *field._?.match(/^([a-z_]+)(?:\.([a-z_]+))?/)
272
+ refl = klass.attr_type(assoc)
273
+
274
+ if refl.respond_to?(:primary_key_name) && refl.macro == :has_many && count._?.upcase == 'COUNT'
275
+ owner_primary_key = "#{klass.quoted_table_name}.#{klass.primary_key}"
276
+ # now we have :has_many association in refl, is this a through association?
277
+ if (through = refl.through_reflection) && (source = refl.source_reflection)
278
+ # has_many through association was found and now we have a few variants:
279
+ # 1) owner.has_many -> through.belongs_to <- source.has_many (many to many, source.macro == :belongs_to )
280
+ # 2) owner.has_many -> through.has_many -> source.belongs_to (many to one through table, source.macro == :has_many)
281
+ colspec = "(SELECT COUNT(*) AS count_all FROM #{refl.quoted_table_name} INNER JOIN #{through.quoted_table_name}" +
282
+ " ON #{source.quoted_table_name}.#{source.macro == :belongs_to ? source.klass.primary_key : through.association_foreign_key}" +
283
+ " = #{through.quoted_table_name}.#{source.macro == :belongs_to ? source.association_foreign_key : through.klass.primary_key}" +
284
+ " WHERE #{through.quoted_table_name}.#{through.primary_key_name} = #{owner_primary_key} )"
285
+ else
286
+ # simple many to one (has_many -> belongs_to) association
287
+ colspec = "(SELECT COUNT(*) as count_all FROM #{refl.quoted_table_name}" +
288
+ " WHERE #{refl.quoted_table_name}.#{refl.primary_key_name} = #{owner_primary_key})"
289
+ end
290
+
291
+ else
292
+ colspec = "#{field}" # don't prepend the table name
293
+ end
271
294
  elsif type.respond_to?(:name_attribute) && (name = type.name_attribute)
272
295
  include = field
273
296
  colspec = "#{type.table_name}.#{name}"
@@ -283,6 +306,11 @@ module Hobo
283
306
  { :include => inclusions }
284
307
  end
285
308
 
309
+ when "includes"
310
+ def_scope do |inclusions|
311
+ { :include => inclusions }
312
+ end
313
+
286
314
  when "search"
287
315
  def_scope do |query, *fields|
288
316
  match_keyword = ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE"
@@ -1,27 +1,39 @@
1
1
  module ActiveRecord
2
2
  module NamedScope
3
- class Scope
4
-
5
- delegate :member_class, :to => :proxy_found
6
-
7
- include Hobo::Scopes::ApplyScopes
3
+
4
+ class Scope
5
+ delegate :member_class, :to => :proxy_found
6
+ include Hobo::Scopes::ApplyScopes
7
+ end
8
8
 
9
- def respond_to?(method, include_private=false)
10
- super || scopes.include?(method) || proxy_scope.respond_to?(method, include_private)
9
+ module ClassMethods
10
+ def scopes
11
+ hash = read_inheritable_attribute(:scopes)
12
+ if hash.nil?
13
+ if respond_to?(:create_automatic_scope)
14
+ write_inheritable_attribute(:scopes, new_automatic_scoping_hash(self))
15
+ else
16
+ # add a default_proc to optimize the next condition
17
+ write_inheritable_attribute(:scopes, Hash.new { |hash, key| nil })
18
+ end
19
+ elsif hash.default_proc.nil? && respond_to?(:create_automatic_scope)
20
+ write_inheritable_attribute(:scopes, new_automatic_scoping_hash(self).merge!(hash))
21
+ else
22
+ hash
23
+ end
11
24
  end
12
25
 
13
26
  private
14
27
 
15
- def method_missing(method, *args, &block)
16
- if scopes.include?(method)
17
- scopes[method].call(self, *args)
18
- else
19
- with_scope :find => proxy_options do
20
- proxy_scope.send(method, *args, &block)
28
+ def new_automatic_scoping_hash(o)
29
+ hash = Hash.new { |hash, key| o.create_automatic_scope(key) && hash[key] }
30
+ hash.meta_eval do
31
+ define_method :include? do |key, *args|
32
+ super(key, *args) || o.create_automatic_scope(key)
21
33
  end
22
34
  end
35
+ hash
23
36
  end
24
-
25
37
  end
26
38
  end
27
39
  end
data/lib/hobo/user.rb CHANGED
@@ -32,7 +32,7 @@ module Hobo
32
32
  end
33
33
 
34
34
  validates_confirmation_of :password, :if => :new_password_required?
35
- password_validations
35
+ validate :validate_password
36
36
  validate :validate_current_password_when_changing_password
37
37
 
38
38
  # Virtual attributes for setting and changing the password
@@ -56,11 +56,6 @@ module Hobo
56
56
  # Additional classmethods for authentication
57
57
  module ClassMethods
58
58
 
59
- # Validation of the plaintext password
60
- def password_validations
61
- validates_length_of :password, :within => 4..40, :if => :new_password_required?
62
- end
63
-
64
59
  def login_attribute=(attr, validate=true)
65
60
  @login_attribute = attr = attr.to_sym
66
61
  unless attr == :login
@@ -177,6 +172,11 @@ module Hobo
177
172
  changing_password? && !authenticated?(current_password) and errors.add :current_password, Hobo::Translations.ht("hobo.messages.current_password_is_not_correct", :default => "is not correct")
178
173
  end
179
174
 
175
+ # Validation of the plaintext password. Override this function
176
+ # to change your validation.
177
+ def validate_password
178
+ errors.add(:password, Hobo::Translations.ht("hobo.messages.validate_password", :default => "must be at least 6 characters long and must not consist solely of lowercase letters.")) if new_password_required? && (password.nil? || password.length<6 || /^[[:lower:]]*$/.match(password))
179
+ end
180
180
  end
181
181
 
182
182
  end
data/lib/hobo.rb CHANGED
@@ -22,7 +22,7 @@ class HoboError < RuntimeError; end
22
22
 
23
23
  module Hobo
24
24
 
25
- VERSION = "1.1.0.pre2"
25
+ VERSION = "1.1.0.pre3"
26
26
 
27
27
  class PermissionDeniedError < RuntimeError; end
28
28
 
@@ -691,7 +691,7 @@ HoboInputMany = {
691
691
  Event.stop(ev);
692
692
  var ul = el.up('ul.input-many'), li = el.up('li.input-many-li');
693
693
 
694
- if(li.id.search(/_-1$/ && ul.immediateDescendants().length>2)>=0) {
694
+ if(li.id.search(/_-1$/)>=0 && ul.immediateDescendants().length>2) {
695
695
  /* if(console) console.log("IE7 messed up again (bug 605)"); */
696
696
  return;
697
697
  }
@@ -140,7 +140,7 @@ end
140
140
  <a:<%= back_link %> param="parent-link">&laquo; <ht key="<%= model_key %>.actions.back" to="<%= back_link %>"><name/></ht></a:<%= back_link %>>
141
141
  <% end -%>
142
142
  <h2 param="heading">
143
- <ht key="<%= model_key %>.show.heading" name="&this.respond_to?(:name) ? this.name : ''">
143
+ <ht key="<%= model_key %>.show.heading" name="&this.respond_to?(:to_s) ? this.to_s : ''">
144
144
  <name/>
145
145
  </ht>
146
146
  </h2>
@@ -175,7 +175,7 @@ end
175
175
  <% if collection -%>
176
176
  <section param="collection-section">
177
177
  <h3 param="collection-heading">
178
- <ht key="<%= model_key %>.collection.heading.<%= (is_user_model ? 'your':'other') %>" >
178
+ <ht key="<%= collection.to_s.tableize %>.collection.heading.<%= (is_user_model ? 'your':'other') %>" >
179
179
  <%= '<Your/>' if is_user_model %><%= collection.to_s.titleize %>
180
180
  </ht>
181
181
  </h3>
@@ -466,14 +466,14 @@ Assuming the context is a blog post...
466
466
  <!-- Renders a comma separated list of links (`<a>`), or "(none)" if the list is empty -->
467
467
  <def tag="links-for-collection"><%= this.empty? ? "(none)" : context_map { a }.join(", ") %></def>
468
468
 
469
- <!-- Renders `this.to_s(:long)`, or `this.strftime(format)` if the `format` attribute is given -->
470
- <def tag="view" for="Date" attrs="format"><%= this && (format ? this.strftime(format) : this.to_s(:long)) %></def>
469
+ <!-- Renders `I18n.l(this, :format => :long)`, or `I18n.l(this, :format => format)` if the `format` attribute is given -->
470
+ <def tag="view" for="Date" attrs="format"><%= this && (format ? I18n.l(this, :format => format) : I18n.l(this, :format => :long)) %></def>
471
471
 
472
- <!-- Renders `this.to_s(:long)`, or `this.strftime(format)` if the `format` attribute is given -->
473
- <def tag="view" for="Time" attrs="format"><%= this && (format ? this.strftime(format) : this.to_s(:long)) %></def>
472
+ <!-- Renders `I18n.l(this, :format => :long)`, or `I18n.l(this, :format => format)` if the `format` attribute is given -->
473
+ <def tag="view" for="Time" attrs="format"><%= this && (format ? I18n.l(this, :format => format) : I18n.l(this, :format => :long)) %></def>
474
474
 
475
- <!-- Renders `this.to_s(:long)`, or `this.strftime(format)` if the `format` attribute is given -->
476
- <def tag="view" for="ActiveSupport::TimeWithZone" attrs="format"><%= this && (format ? this.strftime(format) : this.to_s(:long)) %></def>
475
+ <!-- Renders `I18n.l(this, :format => :long)`, or `I18n.l(this, :format => format)` if the `format` attribute is given -->
476
+ <def tag="view" for="ActiveSupport::TimeWithZone" attrs="format"><%= this && (format ? I18n.l(this, :format => format) : I18n.l(this, :format => :long)) %></def>
477
477
 
478
478
  <!-- Renders `this.to_s`, or `format % this` if the `format` attribute is given -->
479
479
  <def tag="view" for="Numeric" attrs="format"><%= format ? format % this : this.to_s %></def>
@@ -487,12 +487,19 @@ Assuming the context is a blog post...
487
487
  end
488
488
  %></def>
489
489
 
490
- <!-- Renders 'Yes' for true and 'No' for false -->
491
- <def tag="view" for="boolean"><%= this ? 'Yes' : 'No' %></def>
490
+ <!-- Renders 'Yes' for true and 'No' for false with localization support -->
491
+ <def tag="view" for="boolean"><%= this ? ht('hobo.boolean_yes', {:default => 'Yes'}) : ht('hobo.boolean_no', {:default => 'No'}) %></def>
492
492
 
493
493
  <!-- Renders a link (`<a>`) to `this` -->
494
494
  <def tag="view" for="ActiveRecord::Base"><a merge-attrs/></def>
495
495
 
496
+ <!-- Renders `this.to_s` with localization support -->
497
+ <def tag="view" for="HoboFields::LifecycleState">
498
+ <span class='#{this.class.parent.name.downcase}-states #{this.class.parent.name.downcase}-state-#{this.to_s.downcase} state-#{this.to_s.downcase}'>
499
+ <ht key='#{this.class.parent.name.tableize}.lifecycle_states.#{this.to_s.downcase}'><%= this.to_s %></ht>
500
+ </span>
501
+ </def>
502
+
496
503
 
497
504
  <!--
498
505
  A convenience tag used to output a count and a correctly pluralised label. Works with any kind of collection such as an `ActiveRecord` association or an array.
@@ -51,7 +51,8 @@ executed at various points in the ajax request cycle:
51
51
  hiddens = case fields
52
52
  when '*', nil
53
53
  # TODO: Need a better (i.e. extensible) way to eleminate certain fields
54
- this.class.column_names - ['type', 'created_at', 'updated_at']
54
+ # marking a field as attr_protected is one way to eliminate a field
55
+ this.class.column_names - [this.class.inheritance_column, 'created_at', 'updated_at']
55
56
  else
56
57
  comma_split(fields)
57
58
  end
@@ -574,7 +575,7 @@ All the standard ajax attributes *except the callbacks* are supported (see the m
574
575
  else
575
576
  { :type => "button" }
576
577
  end)
577
- label ||= ht("hobo.actions.remove", :default=>"Remove")
578
+ label ||= ht("#{this.class.name.tableize}.actions.remove", :default=>"Remove")
578
579
  confirm = ht("hobo.messages.confirm", :default=>"Are you sure?") if confirm.nil?
579
580
 
580
581
  add_classes!(attributes,
@@ -588,7 +589,7 @@ All the standard ajax attributes *except the callbacks* are supported (see the m
588
589
  else
589
590
  fade = true if fade.nil?
590
591
  attributes[:value] = label
591
- attributes[:onclick] = "Hobo.removeButton(this, '#{url}', #{js_updates(update)}, {fade:#{fade}, confirm: #{confirm.inspect}})"
592
+ attributes[:onclick] = "Hobo.removeButton(this, '#{url}', #{js_updates(update)}, {fade:#{fade}, confirm: '#{confirm}'})"
592
593
  element(:input, attributes, nil, true, true)
593
594
  end
594
595
  end
@@ -622,7 +623,7 @@ All of the standard ajax attributes are supported (see the main taglib documenti
622
623
  new = class_or_assoc.new(fields)
623
624
  new.set_creator(current_user)
624
625
  if can_create?(new)
625
- label ||= ht("#{new.class.name.pluralize.underscore}.actions.new", :default=>"New #{new.class.name.titleize}")
626
+ label ||= ht("#{new.class.name.tableize}.actions.new", :default=>"New #{new.class.name.titleize}")
626
627
  ajax_attributes = { :message => message }
627
628
  class_name = new.class.name.underscore
628
629
  ajax_attributes[:params] = { class_name => fields } unless fields.empty?
@@ -653,7 +654,7 @@ For situations where there are too many target records to practically include in
653
654
  <def tag="select-one" attrs="include-none, blank-message, options, sort, limit, text-method"><%
654
655
  raise Hobo::PermissionDeniedError.new("Not allowed to edit #{this_field}") if !attributes[:disabled] && !can_edit?
655
656
 
656
- blank_message ||= ht("#{this_type.name.underscore}.message.no", :default=>"(No #{this_type.view_hints.model_name})")
657
+ blank_message ||= ht("#{this_type.name.tableize}.message.none", :default=>"(No #{this_type.view_hints.model_name})")
657
658
  limit ||= 100
658
659
 
659
660
  options ||= begin
@@ -780,10 +781,10 @@ If you wish to set `min-chars` to 0, you will require this [patch to controls.js
780
781
  -->
781
782
  <def tag="error-messages">
782
783
  <section class="error-messages" merge-attrs if="&this.errors.length > 0">
783
- <h2 param="heading">To proceed please correct the following:</h2>
784
+ <h2 param="heading"><ht key="hobo.messages.error_correct_following">To proceed please correct the following:</ht></h2>
784
785
  <ul param>
785
786
  <% this.errors.each do |attr, message|; next if message == "..." -%>
786
- <li param><%= this.class.human_attribute_name(attr) unless attr.to_s == 'base' %> <%= message %></li>
787
+ <li param><%= this.class.view_hints.field_name(attr) unless attr.to_s == 'base' %> <%= message %></li>
787
788
  <% end -%>
788
789
  </ul>
789
790
  </section>
@@ -796,12 +797,12 @@ An input for `has_many :through` associations that lets the user chose the items
796
797
  To use this tag, the model of the items the user is chosing *must* have unique names, and the
797
798
  -->
798
799
  <def tag="select-many" attrs="options, targets, remove-label, prompt, disabled, name"><%
799
- prompt ||= "Add #{this_field.titleize.singularize}"
800
+ prompt ||= ht("#{this.name.tableize}.message.none", :default=>"Add #{this.view_hints.model_name}")
800
801
  options ||= this_field_reflection.klass.all(:conditions =>this.conditions).select {|x| can_view?(x)}
801
802
  name ||= param_name_for_this
802
803
 
803
804
  values = this
804
- remove_label ||= ht("hobo.actions.remove", :default=>'Remove')
805
+ remove_label ||= ht("#{this.name.tableize}.actions.remove", :default=>'Remove')
805
806
  -%>
806
807
  <div class="input select-many" merge-attrs>
807
808
  <div style="display:none" class="item-proto">
@@ -172,7 +172,7 @@ See [Filtering stories by status](/tutorials/agility#filtering_stories_by_status
172
172
  <% no_filter ||= "All" %>
173
173
  <form action="&request.request_uri" method="get" class="filter-menu" merge-attrs="id">
174
174
  <div>
175
- <% selected = options.detect {|o| o.to_s==params[param_name.gsub('-', '_')] } %>
175
+ <% selected = options.detect {|o| o.is_a?(Array) ? o[1].to_s==params[param_name.gsub('-', '_')] : o.to_s==params[param_name.gsub('-', '_')] } %>
176
176
  <select-menu name="&param_name" options="&options" selected="&selected"
177
177
  first-option="&no_filter" merge-params/>
178
178
  </div>
@@ -165,8 +165,8 @@ if a given email is associated with an account or not. -->
165
165
  <error-messages param/>
166
166
  <form class="change-password" param>
167
167
  <field-list fields="email_address, current_password, password, password_confirmation" param>
168
- <password-label:>New Password</password-label:>
169
- <password-confirmation-label:>Confirm New Password</password-confirmation-label:>
168
+ <password-label:><ht key="users.new_password">"New Password</ht></password-label:>
169
+ <password-confirmation-label:><ht key="users.confirm_new_password">Confirm New Password</ht></password-confirmation-label:>
170
170
  </field-list>
171
171
 
172
172
  <div class="actions" param="actions">
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hobo
3
3
  version: !ruby/object:Gem::Version
4
- hash: -1876988167
4
+ hash: -1876988186
5
5
  prerelease: true
6
6
  segments:
7
7
  - 1
8
8
  - 1
9
9
  - 0
10
- - pre2
11
- version: 1.1.0.pre2
10
+ - pre3
11
+ version: 1.1.0.pre3
12
12
  platform: ruby
13
13
  authors:
14
14
  - Tom Locke
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-11-12 00:00:00 -05:00
19
+ date: 2011-01-16 00:00:00 -05:00
20
20
  default_executable: hobo
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -73,13 +73,13 @@ dependencies:
73
73
  requirements:
74
74
  - - "="
75
75
  - !ruby/object:Gem::Version
76
- hash: -1876988167
76
+ hash: -1876988186
77
77
  segments:
78
78
  - 1
79
79
  - 1
80
80
  - 0
81
- - pre2
82
- version: 1.1.0.pre2
81
+ - pre3
82
+ version: 1.1.0.pre3
83
83
  type: :runtime
84
84
  version_requirements: *id003
85
85
  - !ruby/object:Gem::Dependency
@@ -90,13 +90,13 @@ dependencies:
90
90
  requirements:
91
91
  - - "="
92
92
  - !ruby/object:Gem::Version
93
- hash: -1876988167
93
+ hash: -1876988186
94
94
  segments:
95
95
  - 1
96
96
  - 1
97
97
  - 0
98
- - pre2
99
- version: 1.1.0.pre2
98
+ - pre3
99
+ version: 1.1.0.pre3
100
100
  type: :runtime
101
101
  version_requirements: *id004
102
102
  - !ruby/object:Gem::Dependency
@@ -107,13 +107,13 @@ dependencies:
107
107
  requirements:
108
108
  - - "="
109
109
  - !ruby/object:Gem::Version
110
- hash: -1876988167
110
+ hash: -1876988186
111
111
  segments:
112
112
  - 1
113
113
  - 1
114
114
  - 0
115
- - pre2
116
- version: 1.1.0.pre2
115
+ - pre3
116
+ version: 1.1.0.pre3
117
117
  type: :runtime
118
118
  version_requirements: *id005
119
119
  description: