hobo 1.3.0.pre31 → 1.3.0

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