hobo 0.8.2 → 0.8.3

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