hobo 1.4.0.pre8 → 2.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/CHANGES-1.4.txt +87 -3
  2. data/TODO-1.4.txt +3 -11
  3. data/VERSION +1 -1
  4. data/app/helpers/hobo_route_helper.rb +61 -20
  5. data/lib/generators/hobo/admin_subsite/USAGE +3 -11
  6. data/lib/generators/hobo/admin_subsite/admin_subsite_generator.rb +4 -1
  7. data/lib/generators/hobo/admin_subsite/templates/{site.css.erb → site.scss.erb} +0 -0
  8. data/lib/generators/hobo/assets/assets_generator.rb +2 -2
  9. data/lib/generators/hobo/assets/templates/{application.css → application.scss} +0 -0
  10. data/lib/generators/hobo/assets/templates/{front.css → front.scss} +0 -0
  11. data/lib/generators/hobo/model/templates/model_injection.rb.erb +1 -0
  12. data/lib/generators/hobo/plugin.rb +3 -1
  13. data/lib/generators/hobo/routes/router.rb +115 -0
  14. data/lib/generators/hobo/routes/templates/hobo_routes.rb.erb +42 -3
  15. data/lib/generators/hobo/setup_wizard/setup_wizard_generator.rb +35 -2
  16. data/lib/generators/hobo/subsite.rb +1 -1
  17. data/lib/generators/hobo/subsite/USAGE +3 -11
  18. data/lib/generators/hobo/subsite/subsite_generator.rb +3 -0
  19. data/lib/generators/hobo/subsite/templates/{site.css.erb → site.scss.erb} +0 -0
  20. data/lib/generators/hobo/subsite_taglib/subsite_taglib_generator.rb +4 -1
  21. data/lib/generators/hobo/user_mailer/templates/activation.erb +1 -1
  22. data/lib/generators/hobo/user_mailer/templates/forgot_password.erb +1 -1
  23. data/lib/generators/hobo/user_mailer/templates/invite.erb +1 -1
  24. data/lib/generators/hobo/user_model/templates/model_injection.rb.erb +1 -0
  25. data/lib/hobo.rb +2 -2
  26. data/lib/hobo/controller.rb +3 -11
  27. data/lib/hobo/controller/model.rb +89 -89
  28. data/lib/hobo/engine.rb +12 -0
  29. data/lib/hobo/extensions/active_record/permissions.rb +15 -24
  30. data/lib/hobo/model.rb +3 -0
  31. data/lib/hobo/model/permissions.rb +1 -1
  32. data/lib/hobo/rapid/generators/rapid/cards.dryml.erb +10 -3
  33. data/lib/hobo/rapid/generators/rapid/forms.dryml.erb +9 -2
  34. data/lib/hobo/rapid/generators/rapid/pages.dryml.erb +11 -4
  35. data/lib/hobo/routes.rb +2 -0
  36. data/test/doctest/hobo/hobo_helper.rdoctest +23 -20
  37. data/test/doctest/hobo/lifecycles.rdoctest +1 -0
  38. data/test/doctest/hobo/model.rdoctest +4 -0
  39. data/test/doctest/hobo/multi_model_forms.rdoctest +2 -0
  40. data/test/doctest/hobo/scopes.rdoctest +18 -5
  41. data/test/doctest/prepare_testapp.rb +4 -2
  42. data/test/irt/generators/admin_subsite.irt +2 -6
  43. data/test/irt/generators/assets.irt +1 -7
  44. data/test/irt/generators/front_controller.irt +1 -3
  45. data/test/irt/generators/model.irt +1 -2
  46. data/test/irt/generators/partials/_account_user_model_tests.rb +1 -3
  47. data/test/irt/generators/partials/_accounts_users_controller_tests.rb +1 -3
  48. data/test/irt/generators/partials/_default_user_model_tests.rb +1 -3
  49. data/test/irt/generators/partials/_default_users_controller_tests.rb +1 -3
  50. data/test/irt/generators/partials/_house_controller_tests.rb +1 -3
  51. data/test/irt/generators/partials/_user_mailer_tests.rb +1 -3
  52. data/test/irt/generators/subsite.irt +5 -6
  53. data/test/irt/generators/user_mailer.irt +2 -0
  54. metadata +12 -18
  55. data/lib/generators/hobo/rapid/templates/themes/clean-sidemenu/public/images/100-ACD3E6-DBE1E5-H.png +0 -0
  56. data/lib/generators/hobo/rapid/templates/themes/clean-sidemenu/public/images/100-DBE1E5-FCFEF5-H.png +0 -0
  57. data/lib/generators/hobo/rapid/templates/themes/clean-sidemenu/public/images/300-3B5F87-ACD3E6-H.png +0 -0
  58. data/lib/generators/hobo/rapid/templates/themes/clean-sidemenu/public/images/spinner.gif +0 -0
  59. data/lib/generators/hobo/rapid/templates/themes/clean-sidemenu/public/stylesheets/clean-sidemenu.css +0 -81
  60. data/lib/generators/hobo/rapid/templates/themes/clean-sidemenu/views/clean-sidemenu.dryml +0 -30
@@ -86,5 +86,17 @@ module Hobo
86
86
  end
87
87
  end
88
88
 
89
+ # hobo_field's rich types let you refer to rich type with symbols:
90
+ # fields do; shout :loud; end.
91
+ # The problem comes if you never use LoudText in your code, rails
92
+ # autoloader will never kick in and autoload
93
+ # app/rich_types/loud_text.rb, so let's preemptively require
94
+ # everything in that directory.
95
+ initializer 'hobo.rich_types' do |app|
96
+ Dir["#{Rails.root}/app/rich_types/*"].each do |file|
97
+ require_dependency file
98
+ end
99
+ end
100
+
89
101
  end
90
102
  end
@@ -68,34 +68,25 @@ ActiveRecord::Associations::HasManyThroughAssociation.class_eval do
68
68
  end
69
69
 
70
70
 
71
- def create!(attrs = nil)
71
+ def create_record_with_user_create(attrs, options, raise = false, &block)
72
72
  klass = @reflection.klass
73
73
  user = acting_user if klass < Hobo::Model
74
- klass.transaction do
75
- object = if attrs
76
- klass.send(:with_scope, :create => attrs) { user ? klass.user_create!(user) : klass.create! }
77
- else
78
- user ? klass.user_create!(user) : klass.create!
79
- end
80
- self << object
81
- object
82
- end
83
- end
84
-
85
-
86
- def create(attrs = nil)
87
- klass = @reflection.klass
88
- user = acting_user if klass < Hobo::Model
89
- klass.transaction do
90
- object = if attrs
91
- klass.send(:with_scope, :create => attrs) { user ? klass.user_create(user) : klass.create }
92
- else
93
- user ? klass.user_create(user) : klass.create
94
- end
95
- self << object
96
- object
74
+ if user
75
+ if attributes.is_a?(Array)
76
+ attributes.collect { |attr| create_record(attr, options, raise, &block) }
77
+ else
78
+ transaction do
79
+ add_to_target(klass.user_create(attributes)) do |record|
80
+ yield(record) if block_given?
81
+ insert_record(record, true, raise)
82
+ end
83
+ end
84
+ end
85
+ else
86
+ create_record_without_user_create(attrs, options, raise, &block)
97
87
  end
98
88
  end
89
+ alias_method_chain :create_record, :user_create
99
90
 
100
91
 
101
92
  def insert_record_with_owner_attributes(record, force = true, raise = false)
@@ -20,6 +20,9 @@ module Hobo
20
20
  inheriting_cattr_reader :default_order
21
21
  alias_method_chain :attributes=, :hobo_type_conversion
22
22
 
23
+ cattr_accessor :hobo_controller
24
+ self.hobo_controller = {}
25
+
23
26
  include Permissions
24
27
  include Lifecycles::ModelExtensions
25
28
  include FindFor
@@ -257,7 +257,7 @@ module Hobo
257
257
  def attribute_protected?(attribute)
258
258
  attribute = attribute.to_s
259
259
 
260
- return true if self.class.attributes_protected_by_default.include? attribute
260
+ return true if self.class.send(:attributes_protected_by_default).include? attribute
261
261
 
262
262
  if !self.class.accessible_attributes.empty?
263
263
  return true if !self.class.accessible_attributes.include?(attribute)
@@ -1,3 +1,10 @@
1
+ <% # routing's not available yet, so we just guess based on presence
2
+ # of methods
3
+ def linkable?(model, action)
4
+ model.hobo_controller[subsite] && model.hobo_controller[subsite].public_instance_methods.include?(action)
5
+ end
6
+
7
+ %>
1
8
  <% each_model do -%>
2
9
  <%
3
10
  name_attribute = model.name_attribute
@@ -10,9 +17,9 @@ if creator_attribute
10
17
  creator_link = creator_refl && linkable?(creator_refl.klass, :show)
11
18
  end
12
19
 
13
- show_link = linkable?(:show)
14
- edit_link = !show_link && linkable?(:edit)
15
- delete_button = !show_link && !edit_link && linkable?(:destroy, :method => :delete)
20
+ show_link = linkable?(model, :show)
21
+ edit_link = !show_link && linkable?(model, :edit)
22
+ delete_button = !show_link && !edit_link && linkable?(model, :destroy)
16
23
 
17
24
  has_actions = edit_link || delete_button
18
25
  has_body = (!show_link && description_attribute) || creator_link || creator_attribute || primary_collection
@@ -1,10 +1,17 @@
1
+ <% # routing's not available yet, so we just guess based on presence
2
+ # of methods
3
+ def linkable?(model, action)
4
+ model.hobo_controller[subsite] && model.hobo_controller[subsite].public_instance_methods.include?(action)
5
+ end
6
+
7
+ %>
1
8
  <% each_controller do -%>
2
9
  <%
3
10
  next unless @controller < Hobo::Controller::Model
4
11
  form_fields = standard_fields :belongs_to, :has_many
5
12
 
6
- cancel_to_show_page = linkable?(:show)
7
- cancel_to_index_page = !cancel_to_index_page && linkable?(:index)
13
+ cancel_to_show_page = linkable?(model, :show)
14
+ cancel_to_index_page = !cancel_to_index_page && linkable?(model, :index)
8
15
  model_key = model.to_s.underscore
9
16
  -%>
10
17
  <def tag="form" for="<%= model.name %>">
@@ -1,3 +1,10 @@
1
+ <% # routing's not available yet, so we just guess based on presence
2
+ # of methods
3
+ def linkable?(model, action)
4
+ model.hobo_controller[subsite] && model.hobo_controller[subsite].public_instance_methods.include?(action)
5
+ end
6
+
7
+ %>
1
8
  <!-- ====== Main Navigation ====== -->
2
9
 
3
10
  <def tag="main-nav">
@@ -15,8 +22,8 @@
15
22
  <% next unless @controller < Hobo::Controller::Model %>
16
23
  <!-- ====== <%= model.name %> Pages ====== -->
17
24
  <%
18
- new_link = linkable?(:new)
19
- new_form = !new_link && linkable?(model, :create, :method => :post)
25
+ new_link = linkable?(model, :new)
26
+ new_form = !new_link && linkable?(model, :create)
20
27
  model_key = model.to_s.underscore
21
28
  -%>
22
29
 
@@ -106,7 +113,7 @@ back_link_human_name = back_link_human_name && back_link_human_name.model_name.h
106
113
  boolean_fields = model.table_exists? ? (model.columns.select { |c| c.type == :boolean }.*.name - model.view_hints.inline_booleans) : []
107
114
  creator = model.creator_attribute
108
115
  creator_link = creator && model.reflections[creator] && linkable?(model.reflections[creator].klass, :show)
109
- edit_link = linkable?(:edit)
116
+ edit_link = linkable?(model, :edit)
110
117
  main_content = model.primary_content_attribute
111
118
  show_fields = standard_fields(:belongs_to).*.to_s - [model.name_attribute, main_content, creator, back_link, *boolean_fields].*.to_s
112
119
 
@@ -118,7 +125,7 @@ if collection
118
125
  owner = model.reverse_reflection(collection)._?.name
119
126
  if owner
120
127
  add_link = collection && linkable?(collection_class, :"new_for_#{owner}")
121
- add_form = !add_link && linkable?(collection_class, :"create_for_#{owner}", :method => :post)
128
+ add_form = !add_link && linkable?(collection_class, :"create_for_#{owner}")
122
129
  end
123
130
  end
124
131
 
@@ -17,10 +17,12 @@ module Hobo
17
17
  end
18
18
 
19
19
  def linkable?(klass, action, options={})
20
+ #raise Hobo::Error, "deprecated"
20
21
  @linkable_keys.member? linkable_key(klass, action, options)
21
22
  end
22
23
 
23
24
  def models_with(wanted_action)
25
+ raise Hobo::Error, "deprecated"
24
26
  @linkable_keys.map do |k|
25
27
  subsite, class_name, action, method = k.split('/')
26
28
  (action == wanted_action.to_s) ? class_name.constantize : nil
@@ -1,5 +1,6 @@
1
1
  doctest: prepare testapp environment
2
2
  doctest_require: '../prepare_testapp'
3
+ doctest_require: 'mocha'
3
4
 
4
5
  # Hobo::Helper
5
6
 
@@ -22,35 +23,37 @@ Useful stuff
22
23
  >> def init_mocha; $stubba = Mocha::Central.new; end
23
24
  >> def mock; Mocha::Mockery.instance.named_mock 'mock'; end
24
25
 
25
- ## `object_url`
26
+ # object_url now uses polymorphic_url, so this is completely out of date. FIXME
26
27
 
27
- Returns a canonical restful URL for a given object. THe Hobo routing is checked and URLs are only returned for routes that exist.
28
+ # ## `object_url`
28
29
 
29
- Note that `object_url` doesn't perform "reverse routing". It knows nothing about attractive URLs you may have declared in your routes file.
30
+ # Returns a canonical restful URL for a given object. THe Hobo routing is checked and URLs are only returned for routes that exist.
30
31
 
31
- Something to link to:
32
+ # Note that `object_url` doesn't perform "reverse routing". It knows nothing about attractive URLs you may have declared in your routes file.
32
33
 
33
- >> init_mocha
34
- >> class Thing; end
35
- >> thing = Thing.new
36
- >> thing.expects(:to_url_path).at_least_once.returns("things/1")
34
+ # Something to link to:
37
35
 
38
- ### Simple 'show' URLs
36
+ # >> init_mocha
37
+ # >> class Thing; end
38
+ # >> thing = Thing.new
39
+ # >> thing.expects(:to_url_path).at_least_once.returns("things/1")
39
40
 
40
- >> Hobo::Routes.expects(:linkable?).with(Thing, :show, {:subsite => ''}).returns(true)
41
- >> View.object_url(thing)
42
- => "/things/1"
41
+ # ### Simple 'show' URLs
43
42
 
44
- Returns nil if Routes says it's not linkable
43
+ # >> Hobo::Routes.expects(:linkable?).with(Thing, :show, {:subsite => ''}).returns(true)
44
+ # >> View.object_url(thing)
45
+ # => "/things/1"
45
46
 
46
- >> Hobo::Routes.expects(:linkable?).with(Thing, :show, {:subsite => ''}).returns(false)
47
- >> View.object_url(thing)
48
- => nil
47
+ # Returns nil if Routes says it's not linkable
49
48
 
50
- A URL to the 'edit' page:
49
+ # >> Hobo::Routes.expects(:linkable?).with(Thing, :show, {:subsite => ''}).returns(false)
50
+ # >> View.object_url(thing)
51
+ # => nil
51
52
 
52
- >> Hobo::Routes.expects(:linkable?).with(Thing, :edit, {:subsite => ''}).returns(true)
53
- >> View.object_url(thing, :edit)
54
- => "/things/1/edit"
53
+ # A URL to the 'edit' page:
54
+
55
+ # >> Hobo::Routes.expects(:linkable?).with(Thing, :edit, {:subsite => ''}).returns(true)
56
+ # >> View.object_url(thing, :edit)
57
+ # => "/things/1/edit"
55
58
 
56
59
 
@@ -26,6 +26,7 @@ A user model for our example:
26
26
  fields do
27
27
  name :string
28
28
  end
29
+ attr_accessible :name
29
30
  end
30
31
 
31
32
  Now the friendship. For now we'll just declare the *invite* part of the lifecycle. We first declare the *states* -- there's only one for now. We then declare the *invite* action. This is the action that first creates the friendship, so we declare it with `create`:
@@ -44,6 +44,7 @@ declaration:
44
44
  title :string, :name => true, :index => true
45
45
  content :text, :primary_content => true
46
46
  end
47
+ attr_accessible :title, :content, :poster, :poster_id
47
48
 
48
49
  belongs_to :poster, :class_name => "User", :creator => true
49
50
 
@@ -122,6 +123,7 @@ object. In this case, you can provide your own name method instead:
122
123
  first_name :string
123
124
  last_name :string
124
125
  end
126
+ attr_accessible :first_name, :last_name
125
127
 
126
128
  def name
127
129
  first_name + ' ' + last_name
@@ -185,6 +187,7 @@ used elsewhere.
185
187
  fields do
186
188
  email_address :string, :login => true, :unique => true
187
189
  end
190
+ attr_accessible :email_address
188
191
  end
189
192
 
190
193
  >> User.login_attribute
@@ -214,6 +217,7 @@ case the `login_attribute` is saved to the field:
214
217
  fields do
215
218
  creator_login :string, :creator => true
216
219
  end
220
+ attr_accessible :creator_login
217
221
  end
218
222
 
219
223
  >> Foo2.creator_attribute
@@ -54,12 +54,14 @@ are the models:
54
54
  hobo_model
55
55
  fields { name :string }
56
56
  has_many :bars, :accessible => true
57
+ attr_accessible :name, :bars
57
58
  end
58
59
  >>
59
60
  class Bar < ActiveRecord::Base
60
61
  hobo_model
61
62
  fields { name :string }
62
63
  belongs_to :foo
64
+ attr_accessible :name, :foo, :foo_id
63
65
  end
64
66
  >> migrate
65
67
 
@@ -36,7 +36,8 @@ Nobody but the computer needs to read the rest of this section.
36
36
 
37
37
  Let's set up a few models for our testing:
38
38
 
39
- >>
39
+ >> File.open("#{Rails.root}/app/models/person.rb", "w") do |f|
40
+ f.write("""
40
41
  class Person < ActiveRecord::Base
41
42
  hobo_model
42
43
 
@@ -47,6 +48,7 @@ Let's set up a few models for our testing:
47
48
  male :boolean
48
49
  timestamps
49
50
  end
51
+ attr_accessible :name, :born_at, :code, :male
50
52
 
51
53
  lifecycle(:key_timestamp_field => false) do
52
54
  state :inactive, :active
@@ -55,19 +57,30 @@ Let's set up a few models for our testing:
55
57
  has_many :friendships
56
58
  has_many :friends, :through => :friendships
57
59
  end
58
-
59
- >>
60
+ """)
61
+ end
62
+ >> File.open("#{Rails.root}/app/models/friendship.rb", "w") do |f|
63
+ f.write("""
60
64
  class Friendship < ActiveRecord::Base
61
65
  hobo_model
62
66
  fields
67
+ attr_accessible :person, :friend
63
68
  belongs_to :person
64
- belongs_to :friend, :class_name => "Person"
69
+ belongs_to :friend, :class_name => 'Person'
70
+ end
71
+ """)
65
72
  end
66
73
 
67
74
  Generate a migration and run it:
68
75
  {.hidden}
69
76
 
70
- >> ActiveRecord::Migration.class_eval(Generators::Hobo::Migration::Migrator.run[0])
77
+ >> File.open("m.txt", "w") {|f| f.write("m\n\n")}
78
+ >> system("rails g hobo:migration < m.txt")
79
+ >> ActionDispatch::Reloader.cleanup!
80
+ >> ActionDispatch::Reloader.prepare!
81
+ >> Person.connection.clear_cache!
82
+ >> Person.connection.schema_cache.clear!
83
+ >> Person.reset_column_information
71
84
  >> Person.columns.*.name
72
85
  => ["id", "name", "born_at", "code", "male", "created_at", "updated_at", "state"]
73
86
  {.hidden}
@@ -3,9 +3,11 @@ require 'tmpdir'
3
3
 
4
4
  TESTAPP_PATH = ENV['TESTAPP_PATH'] || File.join(Dir.tmpdir, 'hobo_testapp')
5
5
  system %(rake test:prepare_testapp TESTAPP_PATH=#{TESTAPP_PATH})
6
- system %(echo "gem 'mocha'" >> #{TESTAPP_PATH}/Gemfile)
6
+ system %(echo "gem 'mocha', :require => false" >> #{TESTAPP_PATH}/Gemfile)
7
+ system %(echo "gem 'test-unit'" >> #{TESTAPP_PATH}/Gemfile)
7
8
  FileUtils.chdir TESTAPP_PATH
8
9
  require "#{TESTAPP_PATH}/config/environment"
9
10
  require 'rails/generators'
10
- Rails::Generators.configure!
11
+ Rails::Generators.configure!(Rails.application.config.generators)
12
+
11
13
 
@@ -1,9 +1,5 @@
1
1
  invoke 'hobo:assets', %w[ -q ]
2
- invoke 'hobo:admin_subsite', %w[ -q --make-front-site=false ]
2
+ invoke 'hobo:admin_subsite', %w[ -q ]
3
3
  desc "Admin Subsite files exist"
4
- files_exist? %w[ app/controllers/admin/admin_site_controller.rb
5
- app/controllers/admin/users_controller.rb
6
- app/helpers/admin/users_helper.rb
7
- app/views/taglibs/admin_site.dryml
8
- ]
4
+ files_exist? %w( app/controllers/admin/admin_site_controller.rb app/controllers/admin/users_controller.rb app/helpers/admin/users_helper.rb app/views/taglibs/admin_site.dryml )
9
5
  test_value_eql? true
@@ -1,11 +1,5 @@
1
1
  desc "hobo:assets invoke"
2
2
  invoke 'hobo:assets', %w(-q)
3
3
 
4
- files_exist? %w[ app/views/taglibs/application.dryml
5
- app/assets/stylesheets/application.css
6
- app/assets/stylesheets/front.css
7
- app/assets/javascripts/application.js
8
- app/assets/javascripts/front.js
9
- app/models/guest.rb
10
- ]
4
+ files_exist? %w( app/views/taglibs/application.dryml app/assets/stylesheets/application.scss app/assets/stylesheets/front.scss app/assets/javascripts/application.js app/assets/javascripts/front.js app/models/guest.rb )
11
5
  test_value_eql? true
@@ -1,8 +1,6 @@
1
1
  invoke 'hobo:front_controller', %w[ -q ]
2
2
  desc "All files exist"
3
- files_exist? %w[ app/controllers/front_controller.rb
4
- app/helpers/front_helper.rb
5
- app/views/front/index.dryml ]
3
+ files_exist? %w( app/controllers/front_controller.rb app/helpers/front_helper.rb app/views/front/index.dryml )
6
4
  test_value_eql? true
7
5
 
8
6
  desc "public/index.html removed"
@@ -5,8 +5,7 @@ eval_file '../partials/_house_model_tests.rb'
5
5
  invoke 'hobo:model', %w(nest/ed alpha:string -q)
6
6
 
7
7
  desc "Nested files exist"
8
- files_exist? %w[ app/models/nest/ed.rb
9
- app/models/nest.rb ]
8
+ files_exist? %w( app/models/nest/ed.rb app/models/nest.rb )
10
9
  test_value_eql? true
11
10
 
12
11
  desc "Nested injection matches"
@@ -1,7 +1,5 @@
1
1
  desc "Account Model files exist"
2
- files_exist? %w[ app/models/account.rb
3
- app/models/account.rb
4
- ]
2
+ files_exist? %w(app/models/account.rb)
5
3
  test_value_eql? true
6
4
 
7
5
  desc "Account Model injection matches"
@@ -1,7 +1,5 @@
1
1
  desc "All controller files exists"
2
- files_exist? %w[ app/controllers/accounts_controller.rb
3
- app/helpers/accounts_helper.rb
4
- ]
2
+ files_exist? %w( app/controllers/accounts_controller.rb app/helpers/accounts_helper.rb )
5
3
  test_value_eql? true
6
4
 
7
5
 
@@ -1,7 +1,5 @@
1
1
  desc "All files exists"
2
- files_exist? %w[ app/models/user.rb
3
- app/models/user.rb
4
- ]
2
+ files_exist? %w( app/models/user.rb )
5
3
  test_value_eql? true
6
4
 
7
5
  desc "User Model injection matches"