hobo 0.8.10 → 0.9.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 (52) hide show
  1. data/CHANGES.txt +126 -2
  2. data/Rakefile +4 -1
  3. data/bin/hobo +1 -1
  4. data/doctest/scopes.rdoctest +11 -4
  5. data/dryml_generators/rapid/cards.dryml.erb +8 -2
  6. data/dryml_generators/rapid/forms.dryml.erb +5 -4
  7. data/dryml_generators/rapid/pages.dryml.erb +150 -65
  8. data/lib/hobo.rb +1 -1
  9. data/lib/hobo/accessible_associations.rb +2 -0
  10. data/lib/hobo/authentication_support.rb +1 -1
  11. data/lib/hobo/controller.rb +11 -3
  12. data/lib/hobo/dryml/dryml_doc.rb +1 -1
  13. data/lib/hobo/fake_initializer.rb +14 -0
  14. data/lib/hobo/hobo_helper.rb +94 -6
  15. data/lib/hobo/lifecycles.rb +17 -2
  16. data/lib/hobo/lifecycles/lifecycle.rb +1 -1
  17. data/lib/hobo/lifecycles/transition.rb +12 -4
  18. data/lib/hobo/model.rb +25 -22
  19. data/lib/hobo/model_controller.rb +42 -37
  20. data/lib/hobo/model_router.rb +11 -7
  21. data/lib/hobo/permissions.rb +12 -10
  22. data/lib/hobo/permissions/associations.rb +1 -1
  23. data/lib/hobo/static_tags +21 -0
  24. data/lib/hobo/user.rb +7 -3
  25. data/lib/hobo/user_controller.rb +7 -7
  26. data/lib/hobo/view_hints.rb +10 -3
  27. data/rails_generators/hobo/USAGE +4 -0
  28. data/rails_generators/hobo_admin_site/USAGE +16 -0
  29. data/rails_generators/hobo_front_controller/hobo_front_controller_generator.rb +11 -2
  30. data/rails_generators/hobo_front_controller/templates/controller.rb +6 -0
  31. data/rails_generators/hobo_front_controller/templates/summary.dryml +103 -0
  32. data/rails_generators/hobo_model_resource/USAGE +38 -0
  33. data/rails_generators/hobo_rapid/USAGE +3 -0
  34. data/rails_generators/hobo_rapid/templates/hobo-rapid.js +7 -3
  35. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +4 -0
  36. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +5 -0
  37. data/rails_generators/hobo_subsite/USAGE +16 -0
  38. data/rails_generators/hobo_subsite/hobo_subsite_generator.rb +1 -1
  39. data/rails_generators/hobo_user_controller/templates/controller.rb +2 -2
  40. data/rails_generators/hobo_user_model/templates/model.rb +6 -1
  41. data/taglibs/rapid.dryml +1 -0
  42. data/taglibs/rapid_core.dryml +4 -4
  43. data/taglibs/rapid_forms.dryml +29 -21
  44. data/taglibs/rapid_generics.dryml +3 -1
  45. data/taglibs/rapid_lifecycles.dryml +14 -9
  46. data/taglibs/rapid_navigation.dryml +1 -1
  47. data/taglibs/rapid_plus.dryml +1 -0
  48. data/taglibs/rapid_summary.dryml +300 -0
  49. data/taglibs/rapid_support.dryml +1 -1
  50. data/taglibs/rapid_user_pages.dryml +21 -19
  51. data/test/permissions/test_permissions.rb +1 -1
  52. metadata +12 -4
@@ -227,14 +227,14 @@ module Hobo
227
227
  def lifecycle_routes
228
228
  model::Lifecycle.creators.values.where.publishable?.*.name.each do |creator|
229
229
  linkable_route("do_#{singular}_#{creator}", "#{plural}/#{creator}", "do_#{creator}",
230
- :conditions => { :method => :post }, :format => false, :linkable_action => creator)
231
- linkable_route("#{singular}_#{creator}", "#{plural}/#{creator}", creator, :conditions => { :method => :get }, :format => false)
230
+ :conditions => { :method => :post }, :linkable_action => creator)
231
+ linkable_route("#{singular}_#{creator}", "#{plural}/#{creator}", creator, :conditions => { :method => :get })
232
232
  end
233
233
  model::Lifecycle.transitions.where.publishable?.*.name.each do |transition|
234
234
  linkable_route("do_#{singular}_#{transition}", "#{plural}/:id/#{transition}", "do_#{transition}",
235
- :conditions => { :method => :put }, :format => false, :linkable_action => transition)
235
+ :conditions => { :method => :put }, :linkable_action => transition)
236
236
  linkable_route("#{singular}_#{transition}", "#{plural}/:id/#{transition}", transition,
237
- :conditions => { :method => :get }, :format => false)
237
+ :conditions => { :method => :get })
238
238
  end
239
239
  end
240
240
 
@@ -252,9 +252,13 @@ module Hobo
252
252
  options.reverse_merge!(:controller => route_with_subsite(plural))
253
253
  name = name_with_subsite(name)
254
254
  route = route_with_subsite(route)
255
- format_route = options.delete(:format) != false
256
- map.named_route(name, route, options)
257
- map.named_route("formatted_#{name}", "#{route}.:format", options) if format_route
255
+ if HoboSupport::RAILS_AT_LEAST_23
256
+ map.named_route(name, "#{route}.:format", options)
257
+ else
258
+ # kick it old-skool
259
+ map.named_route(name, route, options)
260
+ map.named_route("formatted_#{name}", "#{route}.:format", options)
261
+ end
258
262
  true
259
263
  else
260
264
  false
@@ -46,7 +46,7 @@ module Hobo
46
46
  r.set_creator user
47
47
  yield r if block_given?
48
48
  r.user_view(user)
49
- r.send :callback, :after_user_new
49
+ r.with_acting_user(user) { r.send :callback, :after_user_new }
50
50
  end
51
51
  end
52
52
 
@@ -75,7 +75,7 @@ module Hobo
75
75
  def viewable_by?(user, attribute=nil)
76
76
  new.viewable_by?(user, attribute)
77
77
  end
78
-
78
+
79
79
  end
80
80
 
81
81
 
@@ -190,9 +190,9 @@ module Hobo
190
190
  if attribute
191
191
  attribute = attribute.to_s.sub(/\?$/, '').to_sym
192
192
 
193
- # Try the attribute-specic edit-permission method if there is one
193
+ # Try the attribute-specific edit-permission method if there is one
194
194
  if has_hobo_method?(meth = "#{attribute}_edit_permitted?")
195
- with_acting_user(user) { send(meth) }
195
+ return with_acting_user(user) { send(meth) }
196
196
  end
197
197
 
198
198
  # No setter = no edit permission
@@ -375,20 +375,22 @@ module Hobo
375
375
  end
376
376
  end
377
377
  end
378
-
378
+
379
379
  # Best. Name. Ever
380
- def deunknownify_attribute(attr)
380
+ def deunknownify_attribute(attr, remove_globals = true)
381
381
  attr = attr.to_sym
382
-
382
+
383
383
  metaclass.send :remove_method, attr
384
-
384
+
385
385
  if (refl = self.class.reflections[attr]) && refl.macro == :belongs_to
386
386
  # A belongs_to -- restore the underlying fields
387
387
  deunknownify_attribute refl.primary_key_name
388
- deunknownify_attribute refl.options[:foreign_type] if refl.options[:polymorphic]
388
+ deunknownify_attribute(refl.options[:foreign_type], false) if refl.options[:polymorphic]
389
389
  else
390
390
  # A regular field -- restore the dirty tracking methods
391
- ["#{attr}_change", "#{attr}_was", "#{attr}_changed?", :changed?, :changed, :changes].each do |m|
391
+ # if remove_globals is false, skip the top-level methods, as we have already removed them
392
+ to_remove = remove_globals ? [:changed?, :changed, :changes] : []
393
+ (["#{attr}_change", "#{attr}_was", "#{attr}_changed?"] + to_remove).each do |m|
392
394
  metaclass.send :remove_method, m.to_sym
393
395
  end
394
396
  end
@@ -84,7 +84,7 @@ module Hobo
84
84
  end
85
85
 
86
86
 
87
- def create!(attrs = nil)
87
+ def create(attrs = nil)
88
88
  klass = @reflection.klass
89
89
  user = acting_user if klass < Hobo::Model
90
90
  klass.transaction do
@@ -2,6 +2,8 @@ abbr
2
2
  acronym
3
3
  address
4
4
  applet
5
+ article
6
+ audio
5
7
  b
6
8
  basefont
7
9
  bdo
@@ -9,14 +11,20 @@ big
9
11
  blockquote
10
12
  body
11
13
  button
14
+ canvas
12
15
  caption
13
16
  center
14
17
  cite
15
18
  code
16
19
  colgroup
20
+ command
21
+ datagrid
22
+ datalist
17
23
  dd
18
24
  del
25
+ details
19
26
  dfn
27
+ dialog
20
28
  dir
21
29
  div
22
30
  dl
@@ -24,6 +32,7 @@ dt
24
32
  em
25
33
  embed
26
34
  fieldset
35
+ figure
27
36
  font
28
37
  frameset
29
38
  h1
@@ -33,6 +42,7 @@ h4
33
42
  h5
34
43
  h6
35
44
  head
45
+ hgroup
36
46
  i
37
47
  iframe
38
48
  ins
@@ -42,21 +52,30 @@ label
42
52
  legend
43
53
  li
44
54
  map
55
+ mark
45
56
  menu
57
+ meter
58
+ nav
46
59
  noframes
47
60
  noscript
48
61
  object
49
62
  ol
50
63
  optgroup
51
64
  option
65
+ output
52
66
  p
53
67
  pre
68
+ progress
54
69
  q
70
+ rp
71
+ rt
72
+ ruby
55
73
  s
56
74
  samp
57
75
  script
58
76
  select
59
77
  small
78
+ source
60
79
  span
61
80
  strike
62
81
  strong
@@ -69,9 +88,11 @@ textarea
69
88
  tfoot
70
89
  th
71
90
  thead
91
+ time
72
92
  title
73
93
  tr
74
94
  tt
75
95
  u
76
96
  ul
77
97
  var
98
+ video
@@ -37,6 +37,10 @@ module Hobo
37
37
  validate :validate_current_password_when_changing_password
38
38
 
39
39
  # Virtual attributes for setting and changing the password
40
+ # note that :password_confirmation= is also defined by
41
+ # validates_confirmation_of, so this line must follow any
42
+ # validates_confirmation_of statements.
43
+ # https://hobo.lighthouseapp.com/projects/8324-hobo/tickets/530
40
44
  attr_accessor :current_password, :password, :password_confirmation, :type => :password
41
45
 
42
46
  before_save :encrypt_password
@@ -70,7 +74,7 @@ module Hobo
70
74
  end
71
75
  end
72
76
 
73
- attr_reader :login_attribute
77
+ inheriting_cattr_reader :login_attribute
74
78
 
75
79
 
76
80
  # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
@@ -153,7 +157,7 @@ module Hobo
153
157
 
154
158
 
155
159
  def lifecycle_changing_password?
156
- lifecycle.active_step && :password.in?(lifecycle.active_step.parameters)
160
+ self.class.has_lifecycle? && lifecycle.active_step && :password.in?(lifecycle.active_step.parameters)
157
161
  end
158
162
 
159
163
  # Is a new password (and confirmation) required? (i.e. signing up or changing password)
@@ -163,7 +167,7 @@ module Hobo
163
167
 
164
168
 
165
169
  def validate_current_password_when_changing_password
166
- changing_password? && !authenticated?(current_password) and errors.add :current_password, "is not correct"
170
+ changing_password? && !authenticated?(current_password) and errors.add :current_password, ht(:hobo.messages.current_password_is_not_correct, :default["is not correct"])
167
171
  end
168
172
 
169
173
  end
@@ -57,8 +57,8 @@ module Hobo
57
57
  (redirect_to home_page; return) if logged_in?
58
58
 
59
59
  login_attr = model.login_attribute.to_s.titleize.downcase
60
- options.reverse_merge!(:success_notice => "You have logged in.",
61
- :failure_notice => "You did not provide a valid #{login_attr} and password.")
60
+ options.reverse_merge!(:success_notice => ht(:"users.messages.login.success", :default=>["You have logged in."]),
61
+ :failure_notice => ht(:"users.messages.login.error", :login=>login_attr, :default=>["You did not provide a valid #{login_attr} and password."]))
62
62
 
63
63
  if request.post?
64
64
  user = model.authenticate(params[:login], params[:password])
@@ -97,8 +97,8 @@ module Hobo
97
97
  def hobo_do_signup(&b)
98
98
  do_creator_action(:signup) do
99
99
  if valid?
100
- flash[:notice] = "Thanks for signing up!"
101
- flash[:notice] << " You must activate your account before you can log in. Please check your email." unless this.account_active?
100
+ flash[:notice] = ht(:"users.messages.signup.success", :default=>["Thanks for signing up!"])
101
+ flash[:notice] << ht(:"users.messages.signup.must_activate", :default=>[" You must activate your account before you can log in. Please check your email."]) unless this.account_active?
102
102
  end
103
103
  response_block(&b) or if valid?
104
104
  if this.account_active?
@@ -111,7 +111,7 @@ module Hobo
111
111
 
112
112
 
113
113
  def hobo_logout(options={})
114
- options = options.reverse_merge(:notice => "You have logged out.",
114
+ options = options.reverse_merge(:notice => ht(:"users.messages.logout", :default=>["You have logged out."]),
115
115
  :redirect_to => base_url)
116
116
 
117
117
  logout_current_user
@@ -136,7 +136,7 @@ module Hobo
136
136
  do_transition_action :reset_password do
137
137
  response_block(&b) or if valid?
138
138
  self.current_user = this
139
- flash[:notice] = "Your password has been reset"
139
+ flash[:notice] = ht(:"users.messages.reset_password", :default=>["Your password has been reset"])
140
140
  redirect_to home_page
141
141
  end
142
142
  end
@@ -145,7 +145,7 @@ module Hobo
145
145
 
146
146
  def hobo_update_with_account_flash(*args)
147
147
  hobo_update_without_account_flash(*args) do
148
- flash[:notice] = "Changes to your account were saved" if valid? && @this == current_user
148
+ flash[:notice] = ht(:"users.messages.update.success", :default=>["Changes to your account were saved"]) if valid? && @this == current_user
149
149
  yield if block_given?
150
150
  end
151
151
  end
@@ -18,7 +18,7 @@ module Hobo
18
18
  val
19
19
  else
20
20
  arg = if block
21
- block[*args]
21
+ instance_exec(*args, &block)
22
22
  else
23
23
  args.first
24
24
  end
@@ -43,9 +43,16 @@ module Hobo
43
43
  model < ActiveRecord::Acts::List::InstanceMethods &&
44
44
  model.new.try.scope_condition == "1 = 1" }
45
45
 
46
-
46
+ setter :inline_booleans, [] do |*args|
47
+ if args[0] == true
48
+ model.columns.select { |c| c.type == :boolean }.*.name
49
+ else
50
+ args.*.to_s
51
+ end
52
+ end
53
+
47
54
  # Accessors
48
-
55
+
49
56
  class << self
50
57
 
51
58
  def model
@@ -0,0 +1,4 @@
1
+ Description:
2
+
3
+ Adds support for the basic Hobo & DRYML system, not including
4
+ Rapid.
@@ -0,0 +1,16 @@
1
+ Description:
2
+
3
+ Creates a subsite, a namespaced section of your application.
4
+
5
+ The subsite will use app/views/taglibs/<subsite_name>_site.dryml
6
+ instead of app/views/taglibs/appplication.dryml. This allows you
7
+ to easily set different themes and choose different CSS files for
8
+ the subsite.
9
+
10
+ Controllers for the subsite are created in
11
+ app/controllers/<subsite_name>/ and views are also in their own
12
+ subdirectory. This allows you to have two different controllers
13
+ and two different sets of views for the same model.
14
+
15
+ The difference between hobo_admin_site and hobo_subsite is that
16
+ hobo_admin_site limits the subsite to use by administrators only.
@@ -35,6 +35,7 @@ class HoboFrontControllerGenerator < Rails::Generator::NamedBase
35
35
 
36
36
 
37
37
  m.template("index.dryml", File.join('app/views', class_path, file_name, "index.dryml"))
38
+ m.template("summary.dryml", File.join('app/views', class_path, file_name, "summary.dryml"))
38
39
  end
39
40
  end
40
41
 
@@ -46,8 +47,16 @@ class HoboFrontControllerGenerator < Rails::Generator::NamedBase
46
47
  routes_path = File.join(RAILS_ROOT, "config/routes.rb")
47
48
  name = full_class_path
48
49
 
49
- route = (" map.site_search 'search', :controller => '#{name}', :action => 'search'\n" +
50
- " map.root :controller => '#{name}', :action => 'index'")
50
+ root = class_nesting_depth>0 ? class_nesting.underscore : "root"
51
+
52
+ route = " map.site_search 'search', :controller => '#{name}', :action => 'search'\n"
53
+ if class_nesting_depth == 0
54
+ route+= " map.root :controller => '#{name}', :action => 'index'"
55
+ elsif class_nesting_depth == 1
56
+ route+= " map.#{class_nesting.underscore} '/#{class_nesting.underscore}', :controller => '#{name}', :action => 'index'"
57
+ else
58
+ assert false, "no support for class_nesting_depth>1"
59
+ end
51
60
 
52
61
  route_src = File.read(routes_path)
53
62
  return if route_src.include?(route)
@@ -4,6 +4,12 @@ class <%= class_name %>Controller < ApplicationController
4
4
 
5
5
  def index; end
6
6
 
7
+ def summary
8
+ if !current_user.administrator?
9
+ redirect_to user_login_path
10
+ end
11
+ end
12
+
7
13
  def search
8
14
  if params[:query]
9
15
  site_search(params[:query])
@@ -0,0 +1,103 @@
1
+ <page>
2
+ <content:>
3
+ <div class="content-body">
4
+ <h2>Application Summary</h2>
5
+
6
+ <table class="app-summary">
7
+ <tr> <th></th><th></th></tr>
8
+ <tr> <td>Application Name</td> <td><app-name/></td> </tr>
9
+ <tr> <td>Application Location</td> <td><rails-root/></td> </tr>
10
+ <tr> <td>Rails Version</td> <td><rails-version/></td> </tr>
11
+ <tr> <td>Rails Location</td> <td><rails-location/></td> </tr>
12
+ <tr> <td>Mode</td> <td><rails-env/></td> </tr>
13
+ </table>
14
+
15
+ <h3>Change Control</h3>
16
+ <table class="app-summary">
17
+ <tr> <th></th><th></th></tr>
18
+ <tr> <td>Method</td> <td><cms-method/></td> </tr>
19
+ <if test="&cms_method.strip=='git'">
20
+ <tr> <td>Version</td> <td><cms-version/></td> </tr>
21
+ <tr> <td>Date</td> <td><cms-last-commit-time/></td> </tr>
22
+ <tr> <td>Branch</td> <td><cms-branch/></td> </tr>
23
+ <tr> <td>Clean?</td> <td><cms-clean/></td></tr>
24
+ </if>
25
+ </table>
26
+
27
+
28
+ <h3>Gems</h3>
29
+ <table class="app-summary">
30
+ <with-gems>
31
+ <tr if="&first_item?"><th></th><th>Required</th><th>Installed</th><th>Status</th><th>Dependencies</th></tr>
32
+ <tr>
33
+ <td><gem-name/></td>
34
+ <td><gem-version-required/></td>
35
+ <td><gem-version/></td>
36
+ <td><gem-frozen/></td>
37
+ <td><gem-dependencies/></td>
38
+ </tr>
39
+ </with-gems>
40
+ </table>
41
+
42
+ <h3>Plugins</h3>
43
+ <table class="app-summary">
44
+ <with-plugins>
45
+ <tr if="&first_item?"><th></th><th>Location</th><th>Method</th><th>Clean?</th><th>Version</th></tr>
46
+ <tr>
47
+ <td><plugin-name/></td>
48
+ <td><plugin-location/></td>
49
+ <td><plugin-method/></td>
50
+ <td><plugin-clean/></td>
51
+ <td><plugin-version/></td>
52
+ </tr>
53
+ </with-plugins>
54
+ </table>
55
+
56
+ <h3>Environments</h3>
57
+ <table class="app-summary">
58
+ <tr><th></th><th colspan='2'>database</th></tr>
59
+ <with-environments>
60
+ <tr>
61
+ <td><environment-name /></td>
62
+ <td><database-type /></td>
63
+ <td><database-name /></td>
64
+ </tr>
65
+ </with-environments>
66
+ </table>
67
+
68
+ <h2>Models</h2>
69
+ <table class="app-summary">
70
+ <tr><th>Class</th><th>Table</th></tr>
71
+ <with-models>
72
+ <tr>
73
+ <td><model-name/></td>
74
+ <td><model-table-name/></td>
75
+ </tr>
76
+ </with-models>
77
+ </table>
78
+
79
+ <with-models>
80
+ <h3 if="&this.try.table_name"><model-name /></h3>
81
+ <table class="app-summary">
82
+ <with-model-columns>
83
+ <tr if="&first_item?"><th>Column</th><th>Type</th></tr>
84
+ <tr>
85
+ <td><model-column-name/></td>
86
+ <td><model-column-type/></td>
87
+ </tr>
88
+ </with-model-columns>
89
+ </table>
90
+ <table class="app-summary">
91
+ <with-model-associations>
92
+ <tr if="&first_item?"><th>Association</th><th>Macro</th><th>Class</th></tr>
93
+ <tr>
94
+ <td><model-association-name/></td>
95
+ <td><model-association-macro/></td>
96
+ <td><model-association-class-name/></td>
97
+ </tr>
98
+ </with-model-associations>
99
+ </table>
100
+ </with-models>
101
+ </div>
102
+ </content:>
103
+ </page>