hobo 1.1.0.pre2 → 1.1.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
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: