hobo 0.8.3 → 0.8.4

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 (80) hide show
  1. data/CHANGES.txt +330 -0
  2. data/Manifest +12 -4
  3. data/Rakefile +4 -6
  4. data/dryml_generators/rapid/cards.dryml.erb +5 -1
  5. data/dryml_generators/rapid/forms.dryml.erb +8 -10
  6. data/dryml_generators/rapid/pages.dryml.erb +65 -36
  7. data/hobo.gemspec +28 -15
  8. data/lib/active_record/association_collection.rb +3 -22
  9. data/lib/hobo.rb +25 -258
  10. data/lib/hobo/accessible_associations.rb +131 -0
  11. data/lib/hobo/authentication_support.rb +15 -9
  12. data/lib/hobo/composite_model.rb +1 -1
  13. data/lib/hobo/controller.rb +7 -8
  14. data/lib/hobo/dryml.rb +9 -10
  15. data/lib/hobo/dryml/dryml_builder.rb +7 -1
  16. data/lib/hobo/dryml/dryml_doc.rb +161 -0
  17. data/lib/hobo/dryml/dryml_generator.rb +18 -9
  18. data/lib/hobo/dryml/part_context.rb +76 -42
  19. data/lib/hobo/dryml/tag_parameters.rb +1 -0
  20. data/lib/hobo/dryml/taglib.rb +2 -1
  21. data/lib/hobo/dryml/template.rb +39 -29
  22. data/lib/hobo/dryml/template_environment.rb +79 -37
  23. data/lib/hobo/dryml/template_handler.rb +66 -21
  24. data/lib/hobo/guest.rb +2 -10
  25. data/lib/hobo/hobo_helper.rb +125 -53
  26. data/lib/hobo/include_in_save.rb +0 -1
  27. data/lib/hobo/lifecycles.rb +54 -24
  28. data/lib/hobo/lifecycles/actions.rb +95 -31
  29. data/lib/hobo/lifecycles/creator.rb +18 -23
  30. data/lib/hobo/lifecycles/lifecycle.rb +86 -62
  31. data/lib/hobo/lifecycles/state.rb +1 -2
  32. data/lib/hobo/lifecycles/transition.rb +22 -28
  33. data/lib/hobo/model.rb +64 -176
  34. data/lib/hobo/model_controller.rb +67 -54
  35. data/lib/hobo/model_router.rb +5 -2
  36. data/lib/hobo/permissions.rb +397 -0
  37. data/lib/hobo/permissions/associations.rb +167 -0
  38. data/lib/hobo/scopes.rb +15 -38
  39. data/lib/hobo/scopes/association_proxy_extensions.rb +15 -5
  40. data/lib/hobo/scopes/automatic_scopes.rb +43 -18
  41. data/lib/hobo/scopes/named_scope_extensions.rb +2 -2
  42. data/lib/hobo/user.rb +10 -4
  43. data/lib/hobo/user_controller.rb +6 -5
  44. data/lib/hobo/view_hints.rb +58 -0
  45. data/rails_generators/hobo/hobo_generator.rb +7 -3
  46. data/rails_generators/hobo/templates/guest.rb +1 -13
  47. data/rails_generators/hobo_front_controller/hobo_front_controller_generator.rb +1 -1
  48. data/rails_generators/hobo_model/hobo_model_generator.rb +4 -2
  49. data/rails_generators/hobo_model/templates/hints.rb +4 -0
  50. data/rails_generators/hobo_model/templates/model.rb +8 -8
  51. data/rails_generators/hobo_model_controller/hobo_model_controller_generator.rb +10 -0
  52. data/rails_generators/hobo_model_controller/templates/controller.rb +1 -1
  53. data/rails_generators/hobo_rapid/templates/hobo-rapid.js +91 -56
  54. data/rails_generators/hobo_rapid/templates/lowpro.js +15 -15
  55. data/rails_generators/hobo_rapid/templates/reset.css +36 -3
  56. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +13 -17
  57. data/rails_generators/hobo_user_controller/templates/controller.rb +1 -1
  58. data/rails_generators/hobo_user_model/templates/model.rb +18 -16
  59. data/taglibs/core.dryml +60 -18
  60. data/taglibs/rapid.dryml +8 -401
  61. data/taglibs/rapid_core.dryml +586 -0
  62. data/taglibs/rapid_document_tags.dryml +28 -10
  63. data/taglibs/rapid_editing.dryml +92 -55
  64. data/taglibs/rapid_forms.dryml +406 -87
  65. data/taglibs/rapid_generics.dryml +1 -1
  66. data/taglibs/rapid_navigation.dryml +2 -1
  67. data/taglibs/rapid_pages.dryml +7 -16
  68. data/taglibs/rapid_plus.dryml +39 -14
  69. data/taglibs/rapid_support.dryml +1 -1
  70. data/taglibs/rapid_user_pages.dryml +14 -4
  71. data/tasks/{generate_tag_reference.rb → generate_tag_reference.rake} +49 -18
  72. data/tasks/hobo_tasks.rake +16 -0
  73. data/test/permissions/models/models.rb +134 -0
  74. data/test/permissions/models/schema.rb +55 -0
  75. data/test/permissions/models/test.sqlite3 +0 -0
  76. data/test/permissions/test_permissions.rb +436 -0
  77. metadata +27 -14
  78. data/lib/hobo/mass_assignment.rb +0 -64
  79. data/rails_generators/hobo/templates/patch_routing.rb +0 -30
  80. data/uninstall.rb +0 -1
@@ -1,6 +1,6 @@
1
1
  <def tag="card" polymorphic>
2
2
  <div class="card" param="default" merge-attrs>
3
- <header param if-content/>
3
+ <header param/>
4
4
  <div param="body" if="&all_parameters[:body]"/>
5
5
  </div>
6
6
  </def>
@@ -14,7 +14,8 @@
14
14
  </def>
15
15
 
16
16
  <def tag="nav-item">
17
- <set body="&parameters.default"/>
17
+ <% body = parameters.default
18
+ body = h(this.to_s) if body.blank? -%>
18
19
  <li class="#{'current' if (c = scope.current_navigation) && c.downcase == body.downcase.gsub(/<.*?>/, '').strip}"
19
20
  merge-attrs="&attributes - attrs_for(:a)">
20
21
  <a merge-attrs="&attributes & attrs_for(:a)"><%= body %></a>
@@ -4,15 +4,16 @@
4
4
  <head param>
5
5
  <title param><%= strip_tags full_title %></title>
6
6
  <do param="stylesheets">
7
- <stylesheet name="reset"/>
8
- <stylesheet name="hobo-rapid"/>
7
+ <stylesheet name="reset, hobo-rapid"/>
9
8
  <theme-stylesheet/>
10
- <stylesheet name="application"/>
9
+ <stylesheet name="application" param="app-stylesheet"/>
11
10
  </do>
12
11
 
13
12
  <do param="scripts">
14
- <javascript param name="prototype, effects, dragdrop, controls, lowpro, hobo-rapid, application"/>
13
+ <javascript param name="prototype, effects, dragdrop, controls, lowpro, hobo-rapid"/>
15
14
  <if-ie version="lt IE 7" param="fix-ie6"><javascript name="IE7"/></if-ie>
15
+ <do param="custom-scripts"/>
16
+ <javascript param="application-javascript" name="application"/>
16
17
  </do>
17
18
  </head>
18
19
 
@@ -23,7 +24,7 @@
23
24
  <h1 param="app-name"><a href="#{base_url}/"><app-name/></a></h1>
24
25
  <live-search param if="&defined_route? :site_search"/>
25
26
  <account-nav if="&login_url(Hobo::User.default_user_model)" param/>
26
- <main-nav param/>
27
+ <main-nav current="&title" param/>
27
28
  </header>
28
29
  <section with-flash-messages param="content"/>
29
30
  <footer class="page-footer" param/>
@@ -122,9 +123,7 @@
122
123
  </def>
123
124
 
124
125
  <def tag="stylesheet" attrs="name">
125
- <repeat with="&comma_split(name)">
126
- <%= stylesheet_link_tag name, attributes %>
127
- </repeat>
126
+ <%= stylesheet_link_tag *(comma_split(name) + [attributes]) %>
128
127
  </def>
129
128
 
130
129
 
@@ -173,11 +172,3 @@
173
172
  %><do field="&name" param="default"/><%
174
173
  end
175
174
  %></def>
176
-
177
-
178
- <def tag="or-cancel">
179
- <if test="&linkable?">or <a>Cancel</a></if>
180
- <else>
181
- <if test="&linkable?(this.class)">or <a to="&this.class">Cancel</a></if>
182
- </else>
183
- </def>
@@ -41,20 +41,6 @@
41
41
  </def>
42
42
 
43
43
 
44
- <def tag="change-password-form">
45
- <form class="change-password" param>
46
- <field-list fields="current_password, password, password_confirmation" param>
47
- <password-label:>New Password</password-label:>
48
- <password-confirmation-label:>Confirm New Password</password-confirmation-label:>
49
- </field-list>
50
-
51
- <div class="actions" param="actions">
52
- <submit label="Change Password" param/>
53
- </div>
54
- </form>
55
- </def>
56
-
57
-
58
44
  <def tag="sortable-collection" attrs="sortable-options"><%
59
45
  singular_name = this.member_class.name.underscore
60
46
  attributes[:id] ||= "#{singular_name}_ordering"
@@ -79,3 +65,42 @@
79
65
  end # FIXME: Make unobstrusive
80
66
  %>
81
67
  </def>
68
+
69
+
70
+ <def tag="preview-with-more" attrs="name">
71
+ <% name ||= collection_name.pluralize -%>
72
+ <section class="#{name.gsub(' ', '-').dasherize} preview-with-more" param="default">
73
+ <h3 param="heading"><%= name.titleize %></h3>
74
+ <a param="more">More <type-name plural lowercase/>...</a>
75
+ <collection param/>
76
+ </section>
77
+ </def>
78
+
79
+
80
+ <def tag="gravatar" attrs="size, rating">
81
+ <% size ||= 80; rating ||= 'g'; digest = Digest::MD5.hexdigest(this.email_address) -%>
82
+ <a class="gravatar"><img class="gravatar" src="http://www.gravatar.com/avatar/#{digest}?s=#{size}&r=#{rating}" merge-attrs/></a>
83
+ </def>
84
+
85
+
86
+ <def tag="live-search">
87
+ <div class="search">
88
+ <label for="search-field">Search</label><input type="search" class="live-search"/>
89
+ <spinner id="search-spinner"/>
90
+ </div>
91
+ <section class="hidden" id="search-results-panel">
92
+ <h2>Search Results</h2><div param="close-button">close</div>
93
+ <section id="search-results">&nbsp;</section>
94
+ </section>
95
+ </def>
96
+
97
+
98
+ <def tag="filter-menu" attrs="param-name, options, no-filter">
99
+ <% no_filter ||= "All" %>
100
+ <form action="&request.request_uri" method="get" class="filter-menu">
101
+ <div>
102
+ <select-menu name="&param_name" options="&options" selected="&params[param_name.gsub('-', '_')]"
103
+ first-option="&no_filter" merge-params/>
104
+ </div>
105
+ </form>
106
+ </def>
@@ -18,7 +18,7 @@
18
18
  columns + assocs
19
19
  end
20
20
  else
21
- comma_split(fields)
21
+ comma_split(fields.gsub('-', '_'))
22
22
  end
23
23
  field_names -= comma_split(skip) if skip
24
24
  field_names = field_names.select {|f| can_view?(this, f)} unless force_all
@@ -1,5 +1,7 @@
1
1
  <def tag="simple-page">
2
- <page without-live-search without-account-nav without-main-nav merge/>
2
+ <page without-live-search without-main-nav merge>
3
+ <account-nav: replace><dev-user-changer/></account-nav:>
4
+ </page>
3
5
  </def>
4
6
 
5
7
 
@@ -131,13 +133,21 @@
131
133
 
132
134
  <content: param>
133
135
  <header param="content-header">
134
- <h2><name/></h2>
136
+ <h2 param="heading"><name/></h2>
135
137
  </header>
136
138
 
137
139
  <section param="content-body">
138
140
  <error-messages param/>
139
- <h2>Change Password</h2>
140
- <change-password-form/>
141
+ <form class="change-password" param>
142
+ <field-list fields="email_address, current_password, password, password_confirmation" param>
143
+ <password-label:>New Password</password-label:>
144
+ <password-confirmation-label:>Confirm New Password</password-confirmation-label:>
145
+ </field-list>
146
+
147
+ <div class="actions" param="actions">
148
+ <submit label="Save" param/>
149
+ </div>
150
+ </form>
141
151
  </section>
142
152
  </content:>
143
153
 
@@ -1,5 +1,5 @@
1
1
  require 'fileutils'
2
- require 'hobosupport'
2
+
3
3
  ActiveSupport::Dependencies.load_paths << File.dirname(__FILE__) + "/../lib"
4
4
 
5
5
  require 'rexml/xpath'
@@ -115,47 +115,78 @@ end
115
115
 
116
116
 
117
117
  def contents(root)
118
- tags = XPath.match(root, '/*/def')
118
+ tags = XPath.match(root, '/*/*[@tag]')
119
119
  tags.map { |tag| " * #{link_to_tag tag}\n" }.join
120
120
  end
121
121
 
122
122
 
123
123
  def doc_for_taglib(title, root)
124
- tags = XPath.match(root, '/*/def').map { |e| doc_for_tag(e) }.join("\n\n")
124
+ tags = XPath.match(root, '/*/*[@tag]').map { |e| doc_for_tag(e) }.join("\n\n")
125
125
 
126
126
  "# #{title}\n\n" + contents(root) + "\n\n" + tags
127
127
  end
128
128
 
129
- namespace :hobo do
129
+ def index_page(output_files, output_dir)
130
+ markdown = "# Tag Documentation\n\n" + output_files.map do |f|
131
+ link = f.gsub(output_dir+'/', '')
132
+ link_text = link.gsub('_', '\_').gsub(/\.html$/, '')
133
+ "* [#{link_text}](#{link})"
134
+ end.join("\n")
130
135
 
131
- desc "Generate markdown formatted reference docs automatically from DRYML taglibs"
132
- task :generate_tag_reference do
133
- gem 'maruku'
134
- require 'maruku'
136
+ html = Maruku.new(markdown).to_html
137
+
138
+ File.open("#{output_dir}/index.html", 'w') { |f| f.write(html) }
139
+ end
135
140
 
136
- src = ENV['src']
141
+ def process_directory(src, output_dir, output_format)
142
+ raise RuntimeError, "#{output_dir} is not a directory" if File.exists?(output_dir) && !File.directory?(output_dir)
137
143
 
138
- output_dir = ENV['output'] || "taglib-docs"
139
- raise RuntimeError, "#{output_dir} is not a directory" if File.exists?(output_dir) && !File.directory?(output_dir)
144
+ FileUtils.mkdir output_dir unless File.exists? output_dir
140
145
 
141
- FileUtils.mkdir output_dir unless File.exists? output_dir
146
+ dryml_files = File.directory?(src) ? Dir["#{src}/*"] : [src]
142
147
 
143
- dryml_files = File.directory?(src) ? Dir["#{src}/*"] : [src]
148
+ output_files = []
144
149
 
145
- dryml_files.each do |f|
150
+ dryml_files.each do |f|
151
+ if File.directory?(f)
152
+ output_files += process_directory(f, "#{output_dir}/#{File.basename(f)}", output_format)
153
+ elsif f =~ /\.dryml$/
146
154
  basename = File.basename(f).sub(/\.dryml$/, '')
147
155
  title = basename.titleize
148
156
 
149
157
  doc = Hobo::Dryml::Parser::Document.new(File.read(f), f)
150
158
 
151
- markdown = doc_for_taglib(title, doc)
152
- #html = Maruku.new(markdown).to_html
159
+ output = doc.restore_erb_scriptlets(doc_for_taglib(title, doc))
160
+ output = Maruku.new(output).to_html.gsub('&amp;', '&') if output_format == 'html'
153
161
 
154
- output_file = "#{output_dir}/#{basename}.markdown"
162
+ output_file = "#{output_dir}/#{basename}.#{output_format}"
155
163
  puts output_file
156
- File.open(output_file, 'w') { |f| f.write(markdown) }
164
+ File.open(output_file, 'w') { |f| f.write(output) }
165
+ output_files << output_file
157
166
  end
158
167
  end
168
+ output_files
169
+ end
170
+
171
+
172
+
173
+ namespace :hobo do
174
+
175
+ desc "Generate markdown formatted reference docs automatically from DRYML taglibs"
176
+ task :generate_tag_reference => :environment do
177
+ gem 'maruku'
178
+ require 'maruku'
179
+
180
+ src = ENV['src']
181
+
182
+ output_dir = ENV['output'] || "taglib-docs"
183
+
184
+ output_format = ENV['format'] || "markdown"
185
+
186
+ output_files = process_directory(src, output_dir, output_format)
187
+
188
+ index_page(output_files, output_dir)
189
+ end
159
190
 
160
191
  end
161
192
 
@@ -27,6 +27,22 @@ namespace :hobo do
27
27
  end
28
28
 
29
29
  end
30
+
31
+ desc "Run the DRYML generators to generate taglibs in app/views/taglibs/auto"
32
+ task :generate_taglibs => :environment do
33
+ Hobo::Dryml::DrymlGenerator.run
34
+ end
35
+
36
+ desc "Run the standard generators that the hobo command runs."
37
+ task :run_standard_generators do
38
+ exec <<-END
39
+ ruby script/generate hobo --add-routes && \
40
+ ruby script/generate hobo_rapid --import-tags && \
41
+ ruby script/generate hobo_user_model user && \
42
+ ruby script/generate hobo_user_controller user && \
43
+ ruby script/generate hobo_front_controller front --delete-index --add-routes
44
+ END
45
+ end
30
46
 
31
47
  end
32
48
 
@@ -0,0 +1,134 @@
1
+ HOBO_HOME = "#{File.dirname(__FILE__)}/../../../.."
2
+ $:.unshift "#{HOBO_HOME}/hobosupport/lib"
3
+ $:.unshift "#{HOBO_HOME}/hobofields/lib"
4
+ $:.unshift "#{HOBO_HOME}/hobo/lib"
5
+
6
+ require 'rubygems'
7
+ require 'sqlite3'
8
+ require 'activerecord'
9
+ require 'hobosupport'
10
+ require 'hobofields'
11
+ require 'hobo'
12
+
13
+ Hobo::Model.enable
14
+
15
+ module Models
16
+
17
+ extend self
18
+
19
+ HOME = File.dirname(__FILE__)
20
+
21
+ def model(name, &b)
22
+ klass = Object.const_set(name, Class.new(ActiveRecord::Base))
23
+ klass.hobo_model
24
+ klass.class_eval(&b)
25
+ klass.delete_all rescue nil
26
+ end
27
+
28
+ def user_model(name, &b)
29
+ klass = Object.const_set(name, Class.new(ActiveRecord::Base))
30
+ klass.hobo_user_model
31
+ klass.class_eval(&b)
32
+ klass.delete_all rescue nil
33
+ end
34
+
35
+ def create_database_sqlite3
36
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3",
37
+ :database => "#{HOME}/test.sqlite3",
38
+ :timeout => 5000)
39
+ end
40
+
41
+ def init
42
+ create_database_sqlite3
43
+ make_models
44
+ up, down = HoboFields::MigrationGenerator.run
45
+ ActiveRecord::Migration.class_eval(up)
46
+ end
47
+
48
+ def make_models
49
+
50
+ model :Response do
51
+ fields do
52
+ body :string
53
+ end
54
+ belongs_to :user
55
+ belongs_to :recipe
56
+ belongs_to :request
57
+ end
58
+
59
+ model :Comment do
60
+ fields do
61
+ body :string
62
+ end
63
+ belongs_to :user
64
+ belongs_to :recipe
65
+ end
66
+
67
+ model :Request do
68
+ fields do
69
+ body :string
70
+ end
71
+ belongs_to :user
72
+ has_many :responses
73
+ end
74
+
75
+ model :Recipe do
76
+ fields do
77
+ name :string
78
+ body :string
79
+ end
80
+ belongs_to :user, :creator => true
81
+ has_many :responses, :dependent => :destroy
82
+ has_many :comments, :dependent => :destroy
83
+ has_many :images, :dependent => :destroy, :accessible => true
84
+ belongs_to :code_example, :dependent => :destroy, :accessible => true
85
+ has_many :collaboratorships
86
+ has_many :collaborators, :through => :collaboratorships, :source => :user, :accessible => true
87
+
88
+ def create_permitted?; acting_user == user end
89
+ def update_permitted?; acting_user == user end
90
+ def destroy_permitted?; acting_user == user end
91
+ end
92
+
93
+ model :Collaboratorship do
94
+ belongs_to :user
95
+ belongs_to :recipe
96
+ def create_permitted?; recipe.user == acting_user end
97
+ def destroy_permitted?; recipe.user == acting_user end
98
+ end
99
+
100
+ model :Image do
101
+ fields do
102
+ name :string
103
+ end
104
+ belongs_to :recipe
105
+ delegate :user, :to => :recipe
106
+ def create_permitted?; user._?.paid_up? && user == acting_user end
107
+ def update_permitted?; user == acting_user end
108
+ def destroy_permitted?; user == acting_user end
109
+ end
110
+
111
+ model :CodeExample do
112
+ fields do
113
+ filename :string
114
+ end
115
+ end
116
+
117
+ user_model :User do
118
+ fields do
119
+ name :string
120
+ paid_up :boolean
121
+ end
122
+ has_many :recipes
123
+ has_many :comments
124
+ has_many :responses
125
+ has_many :requests
126
+
127
+ lifecycle do
128
+ state :active, :default => true
129
+ end
130
+ end
131
+
132
+ end
133
+
134
+ end
@@ -0,0 +1,55 @@
1
+ # ActiveRecord Schema for testing the Hobo permission system
2
+
3
+ ActiveRecord::Schema.define do
4
+
5
+ create_table "responses", :force => true do |t|
6
+ t.datetime "created_at"
7
+ t.datetime "updated_at"
8
+ t.integer "user_id"
9
+ t.integer "recipe_id"
10
+ t.integer "request_id"
11
+ t.text "body"
12
+ t.boolean "markdown"
13
+ end
14
+
15
+ create_table "comments", :force => true do |t|
16
+ t.text "body"
17
+ t.boolean "markdown"
18
+ t.datetime "created_at"
19
+ t.datetime "updated_at"
20
+ t.integer "user_id"
21
+ t.integer "recipe_id"
22
+ end
23
+
24
+ create_table "requests", :force => true do |t|
25
+ t.text "description"
26
+ t.boolean "markdown"
27
+ t.datetime "created_at"
28
+ t.datetime "updated_at"
29
+ t.integer "user_id"
30
+ t.string "subject"
31
+ end
32
+
33
+ create_table "recipes", :force => true do |t|
34
+ t.string "name"
35
+ t.text "body"
36
+ t.datetime "created_at"
37
+ t.datetime "updated_at"
38
+ t.integer "user_id"
39
+ end
40
+
41
+ create_table "users", :force => true do |t|
42
+ t.string "crypted_password", :limit => 40
43
+ t.string "salt", :limit => 40
44
+ t.string "remember_token"
45
+ t.datetime "remember_token_expires_at"
46
+ t.string "username"
47
+ t.string "email_address"
48
+ t.boolean "administrator", :default => false
49
+ t.datetime "created_at"
50
+ t.datetime "updated_at"
51
+ t.string "state", :default => "active"
52
+ t.datetime "key_timestamp"
53
+ end
54
+
55
+ end