hobo 1.3.0.pre31 → 1.3.0

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 (64) hide show
  1. data/CHANGES-1.1.txt +5253 -0
  2. data/CHANGES.txt +255 -5095
  3. data/VERSION +1 -1
  4. data/hobo.gemspec +1 -2
  5. data/lib/generators/hobo/admin_subsite/USAGE +25 -0
  6. data/lib/generators/hobo/admin_subsite/admin_subsite_generator.rb +2 -1
  7. data/lib/generators/hobo/assets/USAGE +5 -0
  8. data/lib/generators/hobo/assets/templates/application.dryml.erb +1 -1
  9. data/lib/generators/hobo/controller/USAGE +3 -0
  10. data/lib/generators/hobo/i18n/USAGE +3 -0
  11. data/lib/generators/hobo/i18n/templates/app.fr.yml +26 -0
  12. data/lib/generators/hobo/i18n/templates/app.nb.yml +25 -0
  13. data/lib/generators/hobo/i18n/templates/hobo.de.yml +1 -0
  14. data/lib/generators/hobo/i18n/templates/hobo.en.yml +3 -2
  15. data/lib/generators/hobo/i18n/templates/hobo.es.yml +1 -0
  16. data/lib/generators/hobo/i18n/templates/hobo.fr.yml +195 -0
  17. data/lib/generators/hobo/i18n/templates/hobo.it.yml +1 -0
  18. data/lib/generators/hobo/i18n/templates/hobo.nb.yml +198 -0
  19. data/lib/generators/hobo/i18n/templates/hobo.pt-PT.yml +1 -0
  20. data/lib/generators/hobo/i18n/templates/hobo.ru.yml +1 -0
  21. data/lib/generators/hobo/model/USAGE +2 -2
  22. data/lib/generators/hobo/rapid/templates/hobo-rapid.js +15 -30
  23. data/lib/generators/hobo/rapid/templates/themes/clean/public/stylesheets/clean.css +1 -0
  24. data/lib/generators/hobo/resource/USAGE +39 -0
  25. data/lib/generators/hobo/routes/router.rb +2 -2
  26. data/lib/generators/hobo/setup_wizard/setup_wizard_generator.rb +23 -9
  27. data/lib/generators/hobo/subsite.rb +13 -2
  28. data/lib/generators/hobo/subsite/USAGE +24 -0
  29. data/lib/generators/hobo/subsite_taglib/USAGE +4 -0
  30. data/lib/generators/hobo/subsite_taglib/templates/taglib.dryml.erb +1 -1
  31. data/lib/generators/hobo/test_framework/USAGE +2 -0
  32. data/lib/generators/hobo/user_controller/USAGE +3 -0
  33. data/lib/generators/hobo/user_controller/templates/controller.rb.erb +3 -0
  34. data/lib/generators/hobo/user_mailer/USAGE +2 -0
  35. data/lib/generators/hobo/user_model/USAGE +2 -9
  36. data/lib/generators/hobo/user_resource/USAGE +10 -0
  37. data/lib/hobo.rb +1 -1
  38. data/lib/hobo/controller/authentication_support.rb +0 -22
  39. data/lib/hobo/controller/model.rb +15 -13
  40. data/lib/hobo/controller/{user.rb → user_base.rb} +43 -32
  41. data/lib/hobo/extensions/action_controller/hobo_methods.rb +25 -1
  42. data/lib/hobo/extensions/active_record/associations/collection.rb +12 -3
  43. data/lib/hobo/extensions/active_record/associations/reflection.rb +1 -1
  44. data/lib/hobo/extensions/active_record/relation_with_origin.rb +4 -0
  45. data/lib/hobo/helper.rb +6 -1
  46. data/lib/hobo/helper/translations.rb +1 -1
  47. data/lib/hobo/model.rb +55 -19
  48. data/lib/hobo/model/lifecycles.rb +3 -3
  49. data/lib/hobo/model/lifecycles/lifecycle.rb +7 -3
  50. data/lib/hobo/model/permissions.rb +1 -0
  51. data/lib/hobo/model/scopes/automatic_scopes.rb +0 -2
  52. data/lib/hobo/model/view_hints.rb +1 -0
  53. data/lib/hobo/rapid/generators/rapid/forms.dryml.erb +2 -1
  54. data/lib/hobo/rapid/generators/rapid/pages.dryml.erb +10 -11
  55. data/lib/hobo/rapid/helper.rb +4 -3
  56. data/lib/hobo/rapid/taglibs/rapid_core.dryml +92 -67
  57. data/lib/hobo/rapid/taglibs/rapid_editing.dryml +35 -15
  58. data/lib/hobo/rapid/taglibs/rapid_forms.dryml +46 -22
  59. data/lib/hobo/rapid/taglibs/rapid_i18n.dryml +103 -37
  60. data/lib/hobo/rapid/taglibs/rapid_lifecycles.dryml +3 -1
  61. data/lib/hobo/rapid/taglibs/rapid_pages.dryml +3 -3
  62. data/lib/hobo/rapid/taglibs/rapid_plus.dryml +49 -45
  63. data/test/irt/generators/partials/_subsite_taglib_variables.rb +1 -1
  64. metadata +38 -33
@@ -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"
@@ -52,7 +52,7 @@ module Hobo
52
52
 
53
53
  private
54
54
 
55
- def hobo_login(options={})
55
+ def hobo_login(options={}, &block)
56
56
  if logged_in?
57
57
  respond_to do |wants|
58
58
  wants.html { redirect_to home_page }
@@ -62,8 +62,7 @@ module Hobo
62
62
  end
63
63
 
64
64
  login_attr = model.human_attribute_name(model.login_attribute)
65
- options.reverse_merge!(:success_notice => ht(:"#{model.to_s.underscore}.messages.login.success", :default=>["You have logged in."]),
66
- :failure_notice => ht(:"#{model.to_s.underscore}.messages.login.error", :login=>login_attr, :default=>["You did not provide a valid #{login_attr} and password."]))
65
+ options.reverse_merge!(:failure_notice => ht(:"#{model.to_s.underscore}.messages.login.error", :login=>login_attr, :default=>["You did not provide a valid #{login_attr} and password."]))
67
66
 
68
67
  if request.post?
69
68
  user = model.authenticate(params[:login], params[:password])
@@ -71,33 +70,7 @@ module Hobo
71
70
  flash[:error] = options[:failure_notice]
72
71
  hobo_ajax_response if request.xhr? && !performed?
73
72
  else
74
- old_user = current_user
75
- self.current_user = user
76
-
77
- yield if block_given?
78
-
79
- if !user.account_active?
80
- # account not activate - cancel this login
81
- self.current_user = old_user
82
- unless performed?
83
- respond_to do |wants|
84
- wants.html {render :action => :account_disabled}
85
- wants.js {hobo_ajax_response}
86
- end
87
- end
88
- else
89
- if params[:remember_me].present?
90
- current_user.remember_me
91
- create_auth_cookie
92
- end
93
- flash[:notice] ||= options[:success_notice]
94
- unless performed?
95
- respond_to do |wants|
96
- wants.html {redirect_back_or_default(options[:redirect_to] || home_page) }
97
- wants.js {hobo_ajax_response}
98
- end
99
- end
100
- end
73
+ self.sign_user_in(user, options){ yield if block_given?}
101
74
  end
102
75
  end
103
76
  end
@@ -183,6 +156,44 @@ module Hobo
183
156
  end
184
157
  end
185
158
 
159
+ protected
160
+ # If you are authenticating user on your own call this method -
161
+ # hobo will remember signed-in user this way. Arguments:
162
+ # user - user that you want to sign in
163
+ # options - hash with messages (:success_notice, :redirect_to)
164
+ # block - (optional) will be called after assigning current_user
165
+ def sign_user_in(user, options={}, &block)
166
+ options.reverse_merge!(:success_notice => ht(:"#{model.to_s.underscore}.messages.login.success", :default=>["You have logged in."]))
167
+
168
+ old_user = current_user
169
+ self.current_user = user
170
+
171
+ yield if block_given?
172
+
173
+ if !user.account_active?
174
+ # account not activate - cancel this login
175
+ self.current_user = old_user
176
+ unless performed?
177
+ respond_to do |wants|
178
+ wants.html {render :action => :account_disabled}
179
+ wants.js {hobo_ajax_response}
180
+ end
181
+ end
182
+ else
183
+ if params[:remember_me].present?
184
+ current_user.remember_me
185
+ create_auth_cookie
186
+ end
187
+ flash[:notice] ||= options[:success_notice]
188
+ unless performed?
189
+ respond_to do |wants|
190
+ wants.html {redirect_back_or_default(options[:redirect_to] || home_page) }
191
+ wants.js {hobo_ajax_response}
192
+ end
193
+ end
194
+ end
195
+ end
196
+
186
197
  end
187
198
  end
188
199
  end
@@ -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
@@ -17,4 +17,28 @@ ActionController::Base.class_eval do
17
17
  base_url
18
18
  end
19
19
 
20
+ # moved here from authentication_support.rb for easy overriding
21
+ # Redirect as appropriate when an access request fails.
22
+ #
23
+ # The default action is to redirect to the login screen.
24
+ #
25
+ # Override this method in your controllers if you want to have special
26
+ # behavior in case the user is not authorized
27
+ # to access the requested action. For example, a popup window might
28
+ # simply close itself.
29
+ def access_denied(user_model)
30
+ respond_to do |accepts|
31
+ accepts.html do
32
+ store_location
33
+ redirect_to(login_url(user_model))
34
+ end
35
+ accepts.xml do
36
+ headers["Status"] = "Unauthorized"
37
+ headers["WWW-Authenticate"] = %(Basic realm="Web Password")
38
+ render :text => t("hobo.messages.unauthenticated", :default=>["Couldn't authenticate you"]), :status => '401 Unauthorized'
39
+ end
40
+ end
41
+ false
42
+ end
43
+
20
44
  end
@@ -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
@@ -2,6 +2,10 @@ module ActiveRecord
2
2
 
3
3
  class Relation
4
4
  attr_accessor :origin, :origin_attribute
5
+
6
+ def member_class
7
+ @klass
8
+ end
5
9
  end
6
10
 
7
11
  module Associations
@@ -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
@@ -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
@@ -56,18 +45,45 @@ module Hobo
56
45
 
57
46
  WillPaginate::Collection.class_eval do
58
47
  attr_accessor :member_class, :origin, :origin_attribute
48
+
49
+ # make paginate_by_sql, etc. carry metadata
50
+ def replace_with_hobo_metadata(array)
51
+ result = replace_without_hobo_metadata(array)
52
+ self.member_class = array.try.member_class
53
+ self.origin = array.try.origin
54
+ self.origin_attribute = array.try.origin_attribute
55
+ result
56
+ end
57
+ alias_method_chain :replace, :hobo_metadata
59
58
  end
60
59
 
61
- WillPaginate::Finders::Base.class_eval do
62
- def paginate_with_hobo_metadata(*args, &block)
63
- collection = paginate_without_hobo_metadata(*args, &block)
64
- collection.member_class = self.is_a?(ActiveRecord::Relation) ? member_class : self
60
+ WillPaginate::ActiveRecord::Pagination.class_eval do
61
+
62
+ def apply_hobo_metadata(collection)
63
+ klass = Object.instance_method(:class).bind(self).call
64
+ is_relation = klass <= ActiveRecord::Relation
65
+ is_association_proxy = klass <= ActiveRecord::Associations::AssociationProxy
66
+ collection.member_class = (is_relation || is_association_proxy) ? member_class : self
65
67
  collection.origin = try.proxy_owner
66
68
  collection.origin_attribute = try.proxy_reflection._?.name
67
69
  collection
68
70
  end
71
+
72
+ # NOTE: as of will_paginate 3.0.0, the standard paginate method calls the page method.
73
+ # However, it converts an association proxy into a relation first (via adding a limit clause),
74
+ # which causes the proxy_owner and proxy_reflection methods to disappear.
75
+ def paginate_with_hobo_metadata(*args, &block)
76
+ collection = paginate_without_hobo_metadata(*args, &block)
77
+ apply_hobo_metadata(collection)
78
+ end
69
79
  alias_method_chain :paginate, :hobo_metadata
70
80
 
81
+ def page_with_hobo_metadata(*args, &block)
82
+ collection = page_without_hobo_metadata(*args, &block)
83
+ apply_hobo_metadata(collection)
84
+ end
85
+ alias_method_chain :page, :hobo_metadata
86
+
71
87
  end
72
88
 
73
89
  end
@@ -116,7 +132,6 @@ module Hobo
116
132
  end
117
133
  end
118
134
 
119
-
120
135
  module ClassMethods
121
136
 
122
137
  # TODO: should this be an inheriting_cattr_accessor as well? Probably.
@@ -146,6 +161,18 @@ module Hobo
146
161
  send(:login_attribute=, name.to_sym, validate) if options.delete(:login) && respond_to?(:login_attribute=)
147
162
  end
148
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
+
149
176
  private
150
177
 
151
178
  def attrib_names
@@ -162,22 +189,27 @@ module Hobo
162
189
  def belongs_to_with_test_methods(name, options={}, &block)
163
190
  belongs_to_without_test_methods(name, options, &block)
164
191
  refl = reflections[name]
192
+ id_method = refl.options[:primary_key] || refl.klass.primary_key
165
193
  if options[:polymorphic]
166
194
  # TODO: the class lookup in _is? below is incomplete; a polymorphic association to an STI base class
167
195
  # will fail to match an object of a derived type
168
196
  # (ie X belongs_to Y (polymorphic), Z is a subclass of Y; @x.y_is?(some_z) will never pass)
169
197
  class_eval %{
170
198
  def #{name}_is?(target)
171
- 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}
172
200
  end
173
201
  def #{name}_changed?
174
202
  #{refl.primary_key_name}_changed? || #{refl.options[:foreign_type]}_changed?
175
203
  end
176
204
  }
177
205
  else
206
+ id_method = refl.options[:primary_key] || refl.klass.primary_key
178
207
  class_eval %{
179
208
  def #{name}_is?(target)
180
- 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
181
213
  end
182
214
  def #{name}_changed?
183
215
  #{refl.primary_key_name}_changed?
@@ -438,7 +470,11 @@ module Hobo
438
470
  if parts.include?(0)
439
471
  nil
440
472
  else
441
- 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
442
478
  end
443
479
  else
444
480
  value
@@ -103,9 +103,9 @@ module Hobo
103
103
  end
104
104
 
105
105
  def transition(name, change, options={}, &block)
106
- @lifecycle.def_transition(name,
107
- Array(change.keys.first), change.values.first,
108
- block, options)
106
+ change.each do |k,v|
107
+ @lifecycle.def_transition(name, Array(k), v, block, options)
108
+ end
109
109
  end
110
110
 
111
111
  def invariant(&block)
@@ -41,7 +41,7 @@ module Hobo
41
41
  Creator.new(self, name.to_s, on_create, options)
42
42
  end
43
43
 
44
- def self.def_transition(name, start_state, end_states, on_transition, options)
44
+ def self.def_transition(name, start_states, end_state, on_transition, options)
45
45
  class_eval %{
46
46
  def #{name}!(user, attributes=nil)
47
47
  transition(:#{name}, user, attributes)
@@ -50,7 +50,7 @@ module Hobo
50
50
  can_transition?(:#{name}, user)
51
51
  end
52
52
  }
53
- Transition.new(self, name.to_s, start_state, end_states, on_transition, options)
53
+ Transition.new(self, name.to_s, start_states, end_state, on_transition, options)
54
54
  end
55
55
 
56
56
  def self.state_names
@@ -149,7 +149,11 @@ module Hobo
149
149
  end
150
150
 
151
151
  def publishable_transitions_for(user)
152
- available_transitions_for(user).select {|t| t.publishable_by(user, t.available_to, record)}
152
+ record.with_acting_user(user) do
153
+ available_transitions_for(user).select do |t|
154
+ t.publishable_by(user, t.available_to, record)
155
+ end
156
+ end
153
157
  end
154
158
 
155
159
 
@@ -161,6 +161,7 @@ module Hobo
161
161
 
162
162
 
163
163
  def with_acting_user(user)
164
+ return yield if user == acting_user
164
165
  old = acting_user
165
166
  self.acting_user = user
166
167
  result = yield
@@ -288,8 +288,6 @@ module Hobo
288
288
  end
289
289
 
290
290
  when name == "order_by"
291
- # DEPRECATED: use :order instead.
292
- Rails.logger.warn "Automatic scope :order_by has been deprecated: use :order instead."
293
291
  return true if check_only
294
292
 
295
293
  klass = @klass
@@ -73,6 +73,7 @@ module Hobo
73
73
  if arg.nil?
74
74
  @sortable ||= defined?(ActiveRecord::Acts::List::InstanceMethods) &&
75
75
  model < ActiveRecord::Acts::List::InstanceMethods &&
76
+ model.table_exists? &&
76
77
  model.new.try.scope_condition == "1 = 1"
77
78
  else
78
79
  @sortable = arg
@@ -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)
@@ -29,7 +30,7 @@ model_key = model.to_s.underscore
29
30
  </def>
30
31
  <% end -%>
31
32
 
32
- <% transitions.each do |transition| -%>
33
+ <% transitions.uniq{|s| s.name}.each do |transition| -%>
33
34
  <def tag="<%= transition.name.to_s.dasherize %>-form" polymorphic/>
34
35
  <def tag="<%= transition.name.to_s.dasherize %>-form" for="<%= model.name %>">
35
36
  <form lifecycle="<%= transition.name %>" merge param="default">