hobo 0.8.2 → 0.8.3

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 (41) hide show
  1. data/CHANGES.txt +131 -0
  2. data/Manifest +1 -2
  3. data/Rakefile +3 -3
  4. data/dryml_generators/rapid/cards.dryml.erb +1 -1
  5. data/dryml_generators/rapid/forms.dryml.erb +3 -1
  6. data/dryml_generators/rapid/pages.dryml.erb +8 -5
  7. data/hobo.gemspec +8 -8
  8. data/lib/active_record/association_collection.rb +5 -2
  9. data/lib/active_record/association_reflection.rb +14 -5
  10. data/lib/hobo.rb +1 -1
  11. data/lib/hobo/controller.rb +6 -5
  12. data/lib/hobo/dryml.rb +4 -4
  13. data/lib/hobo/dryml/taglib.rb +7 -3
  14. data/lib/hobo/dryml/template_environment.rb +7 -7
  15. data/lib/hobo/hobo_helper.rb +31 -15
  16. data/lib/hobo/include_in_save.rb +9 -1
  17. data/lib/hobo/lifecycles.rb +0 -7
  18. data/lib/hobo/lifecycles/creator.rb +1 -1
  19. data/lib/hobo/lifecycles/lifecycle.rb +8 -3
  20. data/lib/hobo/mass_assignment.rb +64 -0
  21. data/lib/hobo/model.rb +41 -17
  22. data/lib/hobo/model_controller.rb +76 -25
  23. data/lib/hobo/model_router.rb +10 -13
  24. data/lib/hobo/user.rb +13 -13
  25. data/rails_generators/hobo_model/hobo_model_generator.rb +4 -0
  26. data/rails_generators/hobo_model/templates/model.rb +1 -1
  27. data/rails_generators/hobo_rapid/hobo_rapid_generator.rb +0 -2
  28. data/rails_generators/hobo_rapid/templates/hobo-rapid.js +180 -43
  29. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +25 -4
  30. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +0 -2
  31. data/taglibs/core.dryml +8 -5
  32. data/taglibs/rapid.dryml +9 -5
  33. data/taglibs/rapid_document_tags.dryml +1 -1
  34. data/taglibs/rapid_editing.dryml +1 -1
  35. data/taglibs/rapid_forms.dryml +108 -32
  36. data/taglibs/rapid_generics.dryml +2 -2
  37. data/taglibs/rapid_lifecycles.dryml +0 -18
  38. data/taglibs/rapid_user_pages.dryml +8 -41
  39. metadata +7 -7
  40. data/rails_generators/hobo_rapid/templates/nicEditorIcons.gif +0 -0
  41. data/rails_generators/hobo_rapid/templates/nicedit.js +0 -91
@@ -16,8 +16,6 @@ table.new-record textarea, table.new-record input {
16
16
  .inplaceeditor-form, .inplaceeditor-form input {
17
17
  display: inline;
18
18
  }
19
- .inplaceeditor-form .nicEdit-panel .editor_ok_button {display: block; float: right; width: 50px; margin-top: 2px; height:21px; padding-top: 1px;}
20
- .inplaceeditor-form .nicEdit-panel .editor_cancel_link {display: block; float: right; margin: 3px 10px; background: transparent; }
21
19
 
22
20
  /**** Admin ****/
23
21
 
@@ -15,8 +15,7 @@
15
15
  %></def>
16
16
 
17
17
 
18
- <def tag="repeat" attrs="even-odd, join">
19
- <if><%=
18
+ <def tag="repeat" attrs="even-odd, join"><if><%=
20
19
  if even_odd
21
20
  context_map do
22
21
  klass = [attributes[:class], model_id_class, cycle("even", "odd")].compact.join(' ')
@@ -30,9 +29,7 @@
30
29
  res
31
30
  end.join(join)
32
31
  end
33
- end %>
34
- </if>
35
- </def>
32
+ end %></if></def>
36
33
 
37
34
 
38
35
  <def tag="do"><%= parameters.default %></def>
@@ -57,3 +54,9 @@
57
54
  res
58
55
  %></def>
59
56
 
57
+
58
+
59
+ <def tag="fake-field-context" attrs="fake-field, context"><%=
60
+ res = ""
61
+ new_field_context(fake_field, context) { res << parameters.default }
62
+ res %></def>
@@ -102,19 +102,23 @@
102
102
  %></def>
103
103
 
104
104
 
105
- <def tag="name"><%=
105
+ <def tag="name" attrs="if-present, raw"><%=
106
106
  if this.nil?
107
- nil_view
107
+ nil_view unless if_present
108
108
  else
109
109
  name_tag = find_polymorphic_tag("name")
110
110
  if name_tag != "name"
111
- send(name_tag)
111
+ send(name_tag, attributes)
112
112
  elsif this.is_a?(Array) && this.respond_to?(:proxy_reflection)
113
113
  count
114
114
  elsif this.is_a? Class and this < ActiveRecord::Base
115
115
  this.name.pluralize.titleize
116
116
  elsif (name_attr = this.class.try.name_attribute) && can_view?(this, name_attr)
117
- view(merge_attrs(attributes, {:field => name_attr}))
117
+ if raw
118
+ this.send(name_attr)
119
+ else
120
+ view(merge_attrs(attributes, {:field => name_attr}))
121
+ end
118
122
  elsif can_view?(this)
119
123
  this.to_s
120
124
  end
@@ -377,7 +381,7 @@ in the future - use at your own risk. -->
377
381
  <spinner id="search-spinner"/>
378
382
  </div>
379
383
  <section class="hidden" id="search-results-panel">
380
- <h2>Search Results</h2>
384
+ <h2>Search Results</h2><div param="close-button">close</div>
381
385
  <section id="search-results">&nbsp;</section>
382
386
  </section>
383
387
  </def>
@@ -6,7 +6,7 @@
6
6
 
7
7
  <def tag="item-value"><td merge-attrs><do param="default"/></td></def>
8
8
 
9
- <def tag="section-group"><div class="section-group"><div class="section-group-inner" param="default"></div></div></def>
9
+ <def tag="section-group"><div class="section-group"><div class="section-group-inner" merge-attrs param="default"></div></div></def>
10
10
 
11
11
  <!-- section represents a generic document or application section. -->
12
12
  <def tag="section" attrs="empty, with-flash-messages">
@@ -180,7 +180,7 @@
180
180
  <select class="number-editor-bhv #{model_id_class} #{'update:' + comma_split(update).join(':') unless update.blank?}"
181
181
  merge-attrs="&message ? attributes.merge(:hobo_message => message) : attributes">
182
182
  <if test="&this.nil?"><option value=""><%= nil_option || "Choose a value" %></option></if>
183
- <%= options_for_select(options, this) %>
183
+ <%= options_for_select(options.*.to_s, this) %>
184
184
  </select>
185
185
  </def>
186
186
 
@@ -1,28 +1,29 @@
1
- <def tag="hidden-fields" attrs="fields, skip, for-query-string"><%=
2
- skip = comma_split(skip)
1
+ <def tag="hidden-fields" attrs="fields, for-query-string, skip"><%=
3
2
  pairs = if for_query_string
4
3
  query_params.to_a
5
4
  else
6
5
  hiddens = case fields
7
6
  when '*', nil
8
- this.class.column_names - ['type', 'created_at', 'updated_at']
7
+ # TODO: Need a better (i.e. extensible) way to eleminate certain fields
8
+ this.class.column_names - ['type', 'created_at', 'updated_at'] - comma_split(skip)
9
9
  else
10
10
  comma_split(fields)
11
11
  end
12
- pname = this.class.name.underscore
13
12
  hiddens.map do |field|
14
13
  val = this.send(field)
15
- ["#{pname}[#{field}]", val] unless val.nil? ||
16
- field.to_sym.in?(this.class.attr_protected) ||
17
- (this.new_record? && val == this.class.column(field).default)
14
+ param_name = param_name_for(form_this, form_field_path + [field])
15
+ [param_name, val] unless val.nil? ||
16
+ field.to_sym.in?(this.class.attr_protected) ||
17
+ (this.new_record? && val == this.class.column(field).default)
18
18
  end.compact
19
19
  end
20
- pairs.map { |n, v| hidden_field_tag(n, v.to_s) if v && n.not_in?(skip) }.compact.join("\n")
20
+ pairs.map { |n, v| hidden_field_tag(n, v.to_s) if v && n.not_in?(scope.form_field_names) }.compact.join("\n")
21
21
  %></def>
22
22
 
23
23
 
24
- <def tag="form" polymorphic attrs="update, hidden-fields, action, method, web-method, lifecycle, owner"><%=
24
+ <def tag="form" polymorphic attrs="update, hidden-fields, action, method, web-method, lifecycle, owner, multipart"><%=
25
25
  ajax_attrs, html_attrs = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
26
+ html_attrs[:enctype] ||= "multipart/form-data" if multipart
26
27
 
27
28
  new_record = this.try.new_record?
28
29
 
@@ -63,12 +64,14 @@
63
64
  html_attrs[:onsubmit] = [html_attrs[:onsubmit], "#{function}; return false;"].compact.join("; ")
64
65
  end
65
66
 
66
- body, field_names = scope.new_scope :in_form => true do
67
- with_form_context { parameters.default }
67
+ hiddens = nil
68
+ body = with_form_context do
69
+ # It is important to evaluate parameters.default first, in order to populate scope.form_field_names
70
+ b = parameters.default
71
+ hiddens = self.hidden_fields(:fields => hidden_fields) if new_record
72
+ b
68
73
  end
69
74
 
70
- hiddens = hidden_fields(:fields => hidden_fields, :skip => field_names) if new_record
71
-
72
75
  auth_token = if method.nil? || method == 'get' || request_forgery_protection_token.nil?
73
76
  ''
74
77
  else
@@ -77,22 +80,24 @@
77
80
  :value => form_authenticity_token}, nil, true, true)
78
81
  end
79
82
 
80
- page_path = if request.post? || request.put? && params[:page_path]
81
- params[:page_path]
82
- else
83
- view_name.sub(Hobo::Dryml::EMPTY_PAGE, params[:action])
84
- end
85
- page_path_hidden = hidden_field_tag("page_path", page_path) unless method == "get"
86
-
83
+ unless method == "get"
84
+ page_path = if (request.post? || request.put?) && params[:page_path]
85
+ params[:page_path]
86
+ else
87
+ view_name.sub(Hobo::Dryml::EMPTY_PAGE, params[:action])
88
+ end
89
+ page_path_hidden = hidden_field_tag("page_path", page_path)
90
+ end
91
+
87
92
  hiddens_div = element(:div, {:class => "hidden-fields"}, [http_method_hidden, page_path_hidden, auth_token, hiddens].join)
88
93
 
89
94
  body = [hiddens_div, body].join
90
95
 
91
96
  if action.nil? # don't add automatic css classes if the action was specified
92
97
  if web_method
93
- add_classes!(html_attrs, "#{type_id}-#{web_method}-form")
98
+ add_classes!(html_attrs, "#{type_id.dasherize}-#{web_method}-form")
94
99
  else
95
- add_classes!(html_attrs, "#{'new-' if new_record}#{type_id}")
100
+ add_classes!(html_attrs, "#{'new-' if new_record}#{type_id.dasherize}")
96
101
  end
97
102
  end
98
103
 
@@ -125,7 +130,7 @@
125
130
  if refl.options[:through]
126
131
  select_many(attrs)
127
132
  else
128
- raise NotImlementedError, "An input for has-many associations has not been implemented yet"
133
+ raise NotImplementedError, "An input for has-many associations has not been implemented yet"
129
134
  end
130
135
  end
131
136
  else
@@ -198,7 +203,7 @@
198
203
 
199
204
  <!-- Buttons -->
200
205
 
201
- <def tag="remote-method-button" attrs="method, update, label"><%=
206
+ <def tag="remote-method-button" attrs="method, update, label, confirm"><%=
202
207
  ajax_attributes, html_attributes = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
203
208
 
204
209
  url = object_url(this, method, :method => :post)
@@ -206,11 +211,11 @@
206
211
  label ||= method.titleize
207
212
  if update || !ajax_attributes.empty?
208
213
  ajax_attributes[:message] ||= label
209
- func = ajax_updater(url, update, ajax_attributes)
214
+ func = ajax_updater(url, update, ajax_attributes.merge(:confirm => confirm))
210
215
  html_attributes.update(:onclick => "var e = this; " + func, :type =>'button', :value => label)
211
216
  element(:input, html_attributes, nil, true, true)
212
217
  else
213
- button_to(label, url, html_attributes)
218
+ button_to(label, url, html_attributes.merge(:confirm => confirm))
214
219
  end
215
220
  %></def>
216
221
 
@@ -295,7 +300,7 @@
295
300
  options ||= this_field_reflection.klass.all(:conditions => conditions).select {|x| can_view?(x)}
296
301
  #Todo: switch to autocompleter for id_name when too many records, and id_name supported
297
302
  select_options = options.map { |x|
298
- [ name(:with => x, :no_wrapper => true), x.id ]
303
+ [ name(:with => x, :raw => true), x.id ]
299
304
  }
300
305
  select_options = select_options.sort if sort
301
306
  select_options.insert(0, [blank_message, ""]) if include_none || (this.nil? && include_none != false)
@@ -307,9 +312,13 @@
307
312
  </def>
308
313
 
309
314
 
310
- <def tag="name-one" attrs="complete-target, completer">
315
+ <def tag="name-one" attrs="complete-target, completer"><%
316
+ complete_target ||= this_field_reflection.klass
317
+ completer ||= (complete_target.is_a?(Class) ? complete_target : complete_target.class).name_attribute
318
+ %>
311
319
  <input type="text" name="#{param_name_for_this}"
312
320
  class="autocompleter #{type_and_field} complete-on:#{dom_id complete_target}:#{completer}"
321
+ value="&name :no_wrapper => true, :if_present => true"
313
322
  merge-attrs/>
314
323
  <div class="completions-popup" style="display:none"></div>
315
324
  </def>
@@ -331,7 +340,9 @@
331
340
  <section class="error-messages" merge-attrs if="&this.errors.length > 0">
332
341
  <h2 param="heading">To proceed please correct the following:</h2>
333
342
  <ul param>
334
- <li repeat="errors.full_messages" param><%= this %></li>
343
+ <% this.errors.each do |attr, message|; next if message == "..." %>
344
+ <li param><%= attr.titleize %> <%= message %></li>
345
+ <% end %>
335
346
  </ul>
336
347
  </section>
337
348
  </def>
@@ -339,8 +350,12 @@
339
350
 
340
351
  <def tag="select-many" attrs="options, targets, remove-label, prompt">
341
352
  <%
342
- prompt ||= "Add a #{this_field.titleize.singularize}"
343
- options ||= this.member_class.all
353
+ prompt ||= "Add #{a_or_an(:word => this_field.titleize.singularize)}"
354
+ options ||= begin
355
+ conditions = ActiveRecord::Associations::HasManyThroughAssociation.new(this_parent, this_field_reflection).sql_conditions
356
+ this_field_reflection.klass.all(:conditions => conditions).select {|x| can_view?(x)}
357
+ end
358
+
344
359
  values = this
345
360
  %>
346
361
  <div class="input select-many" merge-attrs>
@@ -380,6 +395,67 @@
380
395
  <def tag="select-menu" attrs="options, selected, first-option, first-value">
381
396
  <select merge-attrs param="default">
382
397
  <option value="#{first_value}" unless="&first_option.nil?"><first-option/></option>
383
- <do param="options"><% options_for_select(options.*.to_s, selected.to_s) %></do>
398
+ <do param="options"><% options_for_select(options, selected.to_s) %></do>
384
399
  </select>
385
400
  </def>
401
+
402
+
403
+ <def tag="hidden-id-field">
404
+ <if:id><input type="hidden" name="#{param_name_for_this}" value="#{this}" /></if>
405
+ </def>
406
+
407
+
408
+ <def tag="input-many">
409
+ <set empty="&this.empty?"/>
410
+ <ul class="input-many #{this_field.dasherize} input-many-prefix:#{param_name_for_this}">
411
+ <li repeat class="#{'record-with-errors' unless this.errors.empty?}">
412
+ <error-messages without-heading class="sub-record"/>
413
+ <hidden-id-field/>
414
+ <div class="input-many-item" param="default"/>
415
+ <div class="buttons">
416
+ <button class="remove-item" unless="&this_parent.length == 1">-</button>
417
+ <button class="add-item" if="&last_item?">+</button>
418
+ </div>
419
+ </li>
420
+ <li if="&empty">
421
+ <fake-field-context fake-field="0" context="&this.member_class.new">
422
+ <div class="input-many-item" param="default"/>
423
+ </fake-field-context>
424
+ <div class="buttons">
425
+ <button class="add-item">+</button>
426
+ </div>
427
+ </li>
428
+ </ul>
429
+ </def>
430
+
431
+
432
+ <def tag="input-all">
433
+ <% association_fkey = this_field_reflection.primary_key_name %>
434
+ <ul class="input-all #{this_field.dasherize}">
435
+ <li repeat class="#{'record-with-errors' unless this.errors.empty?}">
436
+ <set-scoped form-field-names="&[]">
437
+ <hidden-id-field/>
438
+ <do param="default"/>
439
+ <hidden-fields skip="&association_fkey"/>
440
+ </set-scoped>
441
+ </li>
442
+ </ul>
443
+ </def>
444
+
445
+
446
+
447
+
448
+
449
+
450
+
451
+
452
+
453
+
454
+
455
+
456
+
457
+
458
+
459
+
460
+
461
+
@@ -12,9 +12,9 @@
12
12
 
13
13
 
14
14
  <def tag="empty-collection-message">
15
- <p class="empty-collection-message" style="#{'display:none' if !this.empty?}" param="default">
15
+ <div class="empty-collection-message" style="#{'display:none' if !this.empty?}" param="default">
16
16
  No <collection-name lowercase/> to display
17
- </p>
17
+ </div>
18
18
  </def>
19
19
 
20
20
 
@@ -1,21 +1,3 @@
1
- <def tag="start-lifecycle" attrs="model, name, parameters">
2
- <%
3
- model = model.constantize if model.is_a?(String)
4
- creator = model::Lifecycle.creators[name]
5
- candidate = creator.candidate(current_user, parameters)
6
- %>
7
- <if with="&candidate">
8
- <div class="lifecycle #{name}" merge-attrs>
9
- <h2 param="heading"><name.titleize/></h2>
10
-
11
- <form lifecycle="#{name}" method="post" param>
12
- <field-list fields="&creator.parameters - parameters.keys" param/>
13
- <submit label="#{name.titleize}" param/>
14
- </form>
15
- </div>
16
- </if>
17
- </def>
18
-
19
1
  <def tag="transition-button" attrs="transition, update, label"><%=
20
2
  transition = transition.name unless transition.is_a?(String)
21
3
  ajax_attributes, html_attributes = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
@@ -4,6 +4,7 @@
4
4
 
5
5
 
6
6
  <def tag="login-page" attrs="remember-me">
7
+ <% remember_me = true if remember_me.nil? %>
7
8
  <simple-page title="Log in" merge>
8
9
 
9
10
  <body: class="login-page" param/>
@@ -33,18 +34,19 @@
33
34
  <item-value><input type="checkbox" name="remember_me" id="remember-me" param="remember-me-input"/></item-value>
34
35
  </labelled-item>
35
36
  </labelled-item-list>
36
- <div class="actions" param="actions">
37
- <submit label='Log in' param/><if test="&signup_url" class='nav-item'> or <a href="&signup_url">Sign up</a></if>
37
+ <div param="actions">
38
+ <submit label='Log in' param/><if test="&signup_url" class='nav-item'>
39
+ or <a href="&signup_url">Sign up</a></if>
38
40
  </div>
39
41
  </form>
40
- <a class="forgot-password" href="&forgot_password_url">Forgot your password?</a>
42
+ <a href="&forgot_password_url" param="forgot-password" if="&forgot_password_url">Forgot your password?</a>
41
43
  </section>
42
44
  </content:>
43
45
  </simple-page>
44
46
  </def>
45
47
 
46
48
 
47
- <def tag="forgot-password-page" attrs="remember-me">
49
+ <def tag="forgot-password-page">
48
50
  <simple-page title="Forgotten Password" merge>
49
51
 
50
52
  <body: class="forgot-password-page" param/>
@@ -53,7 +55,7 @@
53
55
  <header param="content-header">
54
56
  <h2 param="heading">Forgotten Password</h2>
55
57
  <p>Enter the email address you signed up with. If we have it in
56
- our records we'll send you an email allowing you to chose a new
58
+ our records we'll send you an email allowing you to choose a new
57
59
  password.</p>
58
60
  </header>
59
61
 
@@ -67,7 +69,7 @@
67
69
  </item-value>
68
70
  </labelled-item>
69
71
  </labelled-item-list>
70
- <div class="actions" param="actions">
72
+ <div param="actions">
71
73
  <submit label="Send" param/>
72
74
  </div>
73
75
  </form>
@@ -78,41 +80,6 @@
78
80
  </def>
79
81
 
80
82
 
81
- <def tag="forgot-password-page" attrs="remember-me">
82
- <simple-page title="Forgotten Password" merge>
83
-
84
- <body: class="forgot-password-page" param/>
85
-
86
- <content: param>
87
- <header param="content-header">
88
- <h2 param>Forgotten Password</h2>
89
- <p>Enter the email address you signed up with. If we have it in
90
- our records we'll send you an email allowing you to chose a new
91
- password.</p>
92
- </header>
93
-
94
- <section param="content-body">
95
- <form action="" class="forgot-password" param>
96
- <labelled-item-list param>
97
- <labelled-item>
98
- <item-label param="email-address-label">Email Address</item-label>
99
- <item-value>
100
- <input type="text" name="email_address" id="email-address" class="email-address" param="email-address-input" />
101
- </item-value>
102
- </labelled-item>
103
- </labelled-item-list>
104
- <div class="actions" param="actions">
105
- <submit label="Send" param/>
106
- </div>
107
- </form>
108
- </section>
109
-
110
- </content:>
111
-
112
- </simple-page>
113
- </def>
114
-
115
-
116
83
  <def tag="forgot-password-email-sent-page">
117
84
  <simple-page>
118
85
  <body: class="forgot-password-page" param/>