best_in_place 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +0 -1
- data/README.md +32 -3
- data/best_in_place.gemspec +2 -2
- data/lib/assets/javascripts/best_in_place.js +111 -62
- data/lib/assets/javascripts/best_in_place.purr.js +10 -0
- data/lib/best_in_place/controller_extensions.rb +5 -1
- data/lib/best_in_place/display_methods.rb +6 -0
- data/lib/best_in_place/helper.rb +26 -10
- data/lib/best_in_place/version.rb +1 -1
- data/spec/helpers/best_in_place_spec.rb +54 -10
- data/spec/integration/double_init_spec.rb +1 -0
- data/spec/integration/js_spec.rb +173 -1
- data/spec/integration/live_spec.rb +1 -0
- data/spec/integration/text_area_spec.rb +1 -0
- data/test_app/app/assets/javascripts/application.js +1 -1
- data/test_app/app/helpers/users_helper.rb +27 -0
- data/test_app/app/models/user.rb +3 -0
- data/test_app/app/views/users/_form.html.erb +4 -0
- data/test_app/app/views/users/index.html.erb +9 -3
- data/test_app/app/views/users/show.html.erb +26 -0
- data/test_app/db/migrate/20120607172609_add_favorite_movie_to_users.rb +5 -0
- data/test_app/db/migrate/20120616170454_add_money_proc_to_users.rb +6 -0
- data/test_app/db/migrate/20120620165212_add_height_to_user.rb +5 -0
- data/test_app/db/schema.rb +4 -1
- metadata +51 -16
data/README.md
CHANGED
@@ -61,6 +61,7 @@ Options:
|
|
61
61
|
this field.
|
62
62
|
- **:object_name**: Used for overriding the default params key used for the object (the data-object attribute). Useful for e.g. STI scenarios where best_in_place should post to a common controller for different models.
|
63
63
|
- **:data**: Hash of custom data attributes to be added to span. Can be used to provide data to the ajax:success callback.
|
64
|
+
- **:classes**: Additional classes to apply to the best_in_place span. Accepts either a string or Array of strings
|
64
65
|
|
65
66
|
###best_in_place_if
|
66
67
|
**best_in_place_if condition, object, field, OPTIONS**
|
@@ -189,6 +190,15 @@ The 'ajax:success' event is triggered upon success. Use bind:
|
|
189
190
|
|
190
191
|
$('.best_in_place').bind("ajax:success", function(){$(this).closest('tr').effect('highlight'));});
|
191
192
|
|
193
|
+
To bind a callback that is specific to a particular field, use the 'classes' option in the helper method and
|
194
|
+
then bind to that class.
|
195
|
+
|
196
|
+
<%= best_in_place @user, :name, :classes => 'highlight_on_success' %>
|
197
|
+
<%= best_in_place @user, :mail, :classes => 'bounce_on_success' %>
|
198
|
+
|
199
|
+
$('.highlight_on_success').bind("ajax:success", function(){$(this).closest('tr').effect('highlight'));});
|
200
|
+
$('.bounce_on_success').bind("ajax:success", function(){$(this).closest('tr').effect('bounce'));});
|
201
|
+
|
192
202
|
### Providing data to the callback
|
193
203
|
|
194
204
|
Use the :data option to add HTML5 data attributes to the best_in_place span. For example, in your view:
|
@@ -303,13 +313,12 @@ thanks to Rails 3.1. Just begin including the gem in your Gemfile:
|
|
303
313
|
|
304
314
|
gem "best_in_place"
|
305
315
|
|
306
|
-
After that, specify the use of the jquery
|
316
|
+
After that, specify the use of the jquery and best in place
|
307
317
|
javascripts in your application.js, and optionally specify jquery-ui if
|
308
318
|
you want to use jQuery UI datepickers:
|
309
319
|
|
310
320
|
//= require jquery
|
311
321
|
//= require jquery-ui
|
312
|
-
//= require jquery.purr
|
313
322
|
//= require best_in_place
|
314
323
|
|
315
324
|
If you want to use jQuery UI datepickers, you should also install and
|
@@ -340,7 +349,6 @@ After that, install and load all the javascripts from the folder
|
|
340
349
|
**/public/javascripts** in your layouts. They have to be in the order:
|
341
350
|
|
342
351
|
* jquery
|
343
|
-
* jquery.purr
|
344
352
|
* **best_in_place**
|
345
353
|
|
346
354
|
You can automatize this installation by doing
|
@@ -360,6 +368,27 @@ Finally, as for Rails 3.1, just add a binding to prepare all best in place field
|
|
360
368
|
|
361
369
|
---
|
362
370
|
|
371
|
+
## Notification
|
372
|
+
|
373
|
+
Sometimes your in-place updates will fail due to validation or for some other reason. In such case, you'll want to notify the user somehow. **Best in Place** supports doing so through the best_in_place:error event, and has built-in support for notification via jquery.purr, right out of the box.
|
374
|
+
|
375
|
+
To opt into the jquery.purr error notification, just add best_in_place.purr to your javascripts, as described below. If you'd like to develop your own custom form of error notification, you can use best_in_place.purr as an example to guide you.
|
376
|
+
|
377
|
+
###Rails 3.1 and higher
|
378
|
+
|
379
|
+
It's as simple as adding:
|
380
|
+
|
381
|
+
//= require best_in_place.purr
|
382
|
+
|
383
|
+
###Rails 3.0 and lower
|
384
|
+
|
385
|
+
You'll have to load the following additional javascripts, in this order, after loading jquery and **best_in_place**:
|
386
|
+
|
387
|
+
* jquery.purr
|
388
|
+
* **best_in_place.purr**
|
389
|
+
|
390
|
+
---
|
391
|
+
|
363
392
|
## Security
|
364
393
|
|
365
394
|
If the script is used with the Rails Gem no html tags will be allowed unless the sanitize option is set to true, in that case only the tags [*b i u s a strong em p h1 h2 h3 h4 h5 ul li ol hr pre span img*] will be allowed. If the script is used without the gem and with frameworks other than Rails, then you should make sure you are providing the csrf authenticity params as meta tags and you should always escape undesired html tags such as script, object and so forth.
|
data/best_in_place.gemspec
CHANGED
@@ -23,6 +23,6 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_dependency "jquery-rails"
|
24
24
|
|
25
25
|
s.add_development_dependency "rspec-rails", "~> 2.8.0"
|
26
|
-
s.add_development_dependency "nokogiri"
|
27
|
-
s.add_development_dependency "capybara", "
|
26
|
+
s.add_development_dependency "nokogiri"
|
27
|
+
s.add_development_dependency "capybara", "~> 1.1.2"
|
28
28
|
end
|
@@ -59,6 +59,11 @@ BestInPlaceEditor.prototype = {
|
|
59
59
|
},
|
60
60
|
|
61
61
|
abortIfConfirm : function () {
|
62
|
+
if (!this.useConfirm) {
|
63
|
+
this.abort();
|
64
|
+
return;
|
65
|
+
}
|
66
|
+
|
62
67
|
if (confirm("Are you sure you want to discard your changes?")) {
|
63
68
|
this.abort();
|
64
69
|
}
|
@@ -90,7 +95,7 @@ BestInPlaceEditor.prototype = {
|
|
90
95
|
} else if (this.formType == "checkbox") {
|
91
96
|
editor.element.html(this.getValue() ? this.values[1] : this.values[0]);
|
92
97
|
} else {
|
93
|
-
editor.element.html(this.getValue()
|
98
|
+
editor.element.html(this.getValue() !== "" ? this.getValue() : this.nil);
|
94
99
|
}
|
95
100
|
editor.element.trigger(jQuery.Event("best_in_place:update"));
|
96
101
|
},
|
@@ -105,18 +110,19 @@ BestInPlaceEditor.prototype = {
|
|
105
110
|
// Try parent supplied info
|
106
111
|
var self = this;
|
107
112
|
self.element.parents().each(function(){
|
108
|
-
|
109
|
-
self.
|
110
|
-
self.
|
111
|
-
self.
|
112
|
-
self.
|
113
|
-
self.
|
114
|
-
self.
|
115
|
-
self.
|
116
|
-
self.
|
117
|
-
self.
|
118
|
-
self.
|
119
|
-
self.
|
113
|
+
$parent = jQuery(this);
|
114
|
+
self.url = self.url || $parent.attr("data-url");
|
115
|
+
self.collection = self.collection || $parent.attr("data-collection");
|
116
|
+
self.formType = self.formType || $parent.attr("data-type");
|
117
|
+
self.objectName = self.objectName || $parent.attr("data-object");
|
118
|
+
self.attributeName = self.attributeName || $parent.attr("data-attribute");
|
119
|
+
self.activator = self.activator || $parent.attr("data-activator");
|
120
|
+
self.okButton = self.okButton || $parent.attr("data-ok-button");
|
121
|
+
self.cancelButton = self.cancelButton || $parent.attr("data-cancel-button");
|
122
|
+
self.nil = self.nil || $parent.attr("data-nil");
|
123
|
+
self.inner_class = self.inner_class || $parent.attr("data-inner-class");
|
124
|
+
self.html_attrs = self.html_attrs || $parent.attr("data-html-attrs");
|
125
|
+
self.original_content = self.original_content || $parent.attr("data-original-content");
|
120
126
|
});
|
121
127
|
|
122
128
|
// Try Rails-id based if parents did not explicitly supply something
|
@@ -148,6 +154,12 @@ BestInPlaceEditor.prototype = {
|
|
148
154
|
self.sanitize = (self.element.attr("data-sanitize") == "true");
|
149
155
|
}
|
150
156
|
|
157
|
+
if (!self.element.attr("data-use-confirm")) {
|
158
|
+
self.useConfirm = true;
|
159
|
+
} else {
|
160
|
+
self.useConfirm = (self.element.attr("data-use-confirm") != "false");
|
161
|
+
}
|
162
|
+
|
151
163
|
if ((self.formType == "select" || self.formType == "checkbox") && self.collection !== null)
|
152
164
|
{
|
153
165
|
self.values = jQuery.parseJSON(self.collection);
|
@@ -160,10 +172,10 @@ BestInPlaceEditor.prototype = {
|
|
160
172
|
},
|
161
173
|
|
162
174
|
initNil: function() {
|
163
|
-
if (this.element.html()
|
175
|
+
if (this.element.html() === "")
|
164
176
|
{
|
165
|
-
this.isNil = true
|
166
|
-
this.element.html(this.nil)
|
177
|
+
this.isNil = true;
|
178
|
+
this.element.html(this.nil);
|
167
179
|
}
|
168
180
|
},
|
169
181
|
|
@@ -207,7 +219,7 @@ BestInPlaceEditor.prototype = {
|
|
207
219
|
|
208
220
|
loadSuccessCallback : function(data) {
|
209
221
|
var response = jQuery.parseJSON(jQuery.trim(data));
|
210
|
-
if (response
|
222
|
+
if (response !== null && response.hasOwnProperty("display_as")) {
|
211
223
|
this.element.attr("data-original-content", this.element.html());
|
212
224
|
this.original_content = this.element.html();
|
213
225
|
this.element.html(response["display_as"]);
|
@@ -222,12 +234,7 @@ BestInPlaceEditor.prototype = {
|
|
222
234
|
loadErrorCallback : function(request, error) {
|
223
235
|
this.element.html(this.oldValue);
|
224
236
|
|
225
|
-
|
226
|
-
jQuery.each(jQuery.parseJSON(request.responseText), function(index, value) {
|
227
|
-
if( typeof(value) == "object") {value = index + " " + value.toString(); }
|
228
|
-
var container = jQuery("<span class='flash-error'></span>").html(value);
|
229
|
-
container.purr();
|
230
|
-
});
|
237
|
+
this.element.trigger(jQuery.Event("best_in_place:error"), [request, error])
|
231
238
|
this.element.trigger(jQuery.Event("ajax:error"));
|
232
239
|
|
233
240
|
// Binding back after being clicked
|
@@ -258,25 +265,39 @@ BestInPlaceEditor.prototype = {
|
|
258
265
|
BestInPlaceEditor.forms = {
|
259
266
|
"input" : {
|
260
267
|
activateForm : function() {
|
261
|
-
var output = '
|
262
|
-
|
263
|
-
|
264
|
-
|
268
|
+
var output = $(document.createElement('form'))
|
269
|
+
.addClass('form_in_place')
|
270
|
+
.attr('action', 'javascript:void(0);')
|
271
|
+
.attr('style', 'display:inline');
|
272
|
+
var input_elt = $(document.createElement('input'))
|
273
|
+
.attr('type', 'text')
|
274
|
+
.attr('name', this.attributeName)
|
275
|
+
.val(this.display_value);
|
276
|
+
if(this.inner_class !== null) {
|
277
|
+
input_elt.addClass(this.inner_class);
|
265
278
|
}
|
266
|
-
output
|
267
|
-
if
|
268
|
-
output
|
279
|
+
output.append(input_elt);
|
280
|
+
if(this.okButton) {
|
281
|
+
output.append(
|
282
|
+
$(document.createElement('input'))
|
283
|
+
.attr('type', 'submit')
|
284
|
+
.attr('value', this.okButton)
|
285
|
+
)
|
269
286
|
}
|
270
|
-
if
|
271
|
-
output
|
287
|
+
if(this.cancelButton) {
|
288
|
+
output.append(
|
289
|
+
$(document.createElement('input'))
|
290
|
+
.attr('type', 'button')
|
291
|
+
.attr('value', this.cancelButton)
|
292
|
+
)
|
272
293
|
}
|
273
|
-
|
294
|
+
|
274
295
|
this.element.html(output);
|
275
296
|
this.setHtmlAttributes();
|
276
297
|
this.element.find("input[type='text']")[0].select();
|
277
298
|
this.element.find("form").bind('submit', {editor: this}, BestInPlaceEditor.forms.input.submitHandler);
|
278
299
|
if (this.cancelButton) {
|
279
|
-
this.element.find("input[type='button']").bind('click', {editor: this}, BestInPlaceEditor.forms.input.cancelButtonHandler)
|
300
|
+
this.element.find("input[type='button']").bind('click', {editor: this}, BestInPlaceEditor.forms.input.cancelButtonHandler);
|
280
301
|
}
|
281
302
|
this.element.find("input[type='text']").bind('blur', {editor: this}, BestInPlaceEditor.forms.input.inputBlurHandler);
|
282
303
|
this.element.find("input[type='text']").bind('keyup', {editor: this}, BestInPlaceEditor.forms.input.keyupHandler);
|
@@ -331,13 +352,20 @@ BestInPlaceEditor.forms = {
|
|
331
352
|
|
332
353
|
"date" : {
|
333
354
|
activateForm : function() {
|
334
|
-
var that
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
355
|
+
var that = this,
|
356
|
+
output = $(document.createElement('form'))
|
357
|
+
.addClass('form_in_place')
|
358
|
+
.attr('action', 'javascript:void(0);')
|
359
|
+
.attr('style', 'display:inline'),
|
360
|
+
input_elt = $(document.createElement('input'))
|
361
|
+
.attr('type', 'text')
|
362
|
+
.attr('name', this.attributeName)
|
363
|
+
.attr('value', this.sanitizeValue(this.display_value));
|
364
|
+
if(this.inner_class !== null) {
|
365
|
+
input_elt.addClass(this.inner_class);
|
339
366
|
}
|
340
|
-
output
|
367
|
+
output.append(input_elt)
|
368
|
+
|
341
369
|
this.element.html(output);
|
342
370
|
this.setHtmlAttributes();
|
343
371
|
this.element.find('input')[0].select();
|
@@ -370,14 +398,24 @@ BestInPlaceEditor.forms = {
|
|
370
398
|
|
371
399
|
"select" : {
|
372
400
|
activateForm : function() {
|
373
|
-
var output
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
401
|
+
var output = $(document.createElement('form'))
|
402
|
+
.attr('action', 'javascript:void(0)')
|
403
|
+
.attr('style', 'display:inline');
|
404
|
+
selected = '',
|
405
|
+
oldValue = this.oldValue,
|
406
|
+
select_elt = $(document.createElement('select'));
|
407
|
+
jQuery.each(this.values, function (index, value) {
|
408
|
+
var option_elt = $(document.createElement('option'))
|
409
|
+
// .attr('value', value[0])
|
410
|
+
.val(value[0])
|
411
|
+
.html(value[1]);
|
412
|
+
if(value[1] == oldValue) {
|
413
|
+
option_elt.attr('selected', 'selected');
|
414
|
+
}
|
415
|
+
select_elt.append(option_elt);
|
416
|
+
});
|
417
|
+
output.append(select_elt);
|
418
|
+
|
381
419
|
this.element.html(output);
|
382
420
|
this.setHtmlAttributes();
|
383
421
|
this.element.find("select").bind('change', {editor: this}, BestInPlaceEditor.forms.select.blurHandler);
|
@@ -388,6 +426,7 @@ BestInPlaceEditor.forms = {
|
|
388
426
|
|
389
427
|
getValue : function() {
|
390
428
|
return this.sanitizeValue(this.element.find("select").val());
|
429
|
+
// return this.element.find("select").val();
|
391
430
|
},
|
392
431
|
|
393
432
|
blurHandler : function(event) {
|
@@ -401,7 +440,7 @@ BestInPlaceEditor.forms = {
|
|
401
440
|
|
402
441
|
"checkbox" : {
|
403
442
|
activateForm : function() {
|
404
|
-
var newValue = Boolean(this.oldValue != this.values[1]);
|
443
|
+
var newValue = Boolean(this.oldValue.toLowerCase() != this.values[1].toLowerCase());
|
405
444
|
var output = newValue ? this.values[1] : this.values[0];
|
406
445
|
this.element.html(output);
|
407
446
|
this.setHtmlAttributes();
|
@@ -409,7 +448,7 @@ BestInPlaceEditor.forms = {
|
|
409
448
|
},
|
410
449
|
|
411
450
|
getValue : function() {
|
412
|
-
return Boolean(this.element.html() == this.values[1]);
|
451
|
+
return Boolean(this.element.html().toLowerCase() == this.values[1].toLowerCase());
|
413
452
|
}
|
414
453
|
},
|
415
454
|
|
@@ -418,18 +457,28 @@ BestInPlaceEditor.forms = {
|
|
418
457
|
// grab width and height of text
|
419
458
|
width = this.element.css('width');
|
420
459
|
height = this.element.css('height');
|
421
|
-
|
422
|
-
// construct
|
423
|
-
var output
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
460
|
+
|
461
|
+
// construct form
|
462
|
+
var output = $(document.createElement('form'))
|
463
|
+
.attr('action', 'javascript:void(0)')
|
464
|
+
.attr('style', 'display:inline')
|
465
|
+
.append($(document.createElement('textarea'))
|
466
|
+
.val(this.sanitizeValue(this.display_value)));
|
467
|
+
if(this.okButton) {
|
468
|
+
output.append(
|
469
|
+
$(document.createElement('input'))
|
470
|
+
.attr('type', 'submit')
|
471
|
+
.attr('value', this.okButton)
|
472
|
+
);
|
428
473
|
}
|
429
|
-
if
|
430
|
-
output
|
474
|
+
if(this.cancelButton) {
|
475
|
+
output.append(
|
476
|
+
$(document.createElement('input'))
|
477
|
+
.attr('type', 'button')
|
478
|
+
.attr('value', this.cancelButton)
|
479
|
+
)
|
431
480
|
}
|
432
|
-
|
481
|
+
|
433
482
|
this.element.html(output);
|
434
483
|
this.setHtmlAttributes();
|
435
484
|
|
@@ -440,7 +489,7 @@ BestInPlaceEditor.forms = {
|
|
440
489
|
this.element.find("textarea")[0].focus();
|
441
490
|
this.element.find("form").bind('submit', {editor: this}, BestInPlaceEditor.forms.textarea.submitHandler);
|
442
491
|
if (this.cancelButton) {
|
443
|
-
this.element.find("input[type='button']").bind('click', {editor: this}, BestInPlaceEditor.forms.textarea.cancelButtonHandler)
|
492
|
+
this.element.find("input[type='button']").bind('click', {editor: this}, BestInPlaceEditor.forms.textarea.cancelButtonHandler);
|
444
493
|
}
|
445
494
|
this.element.find("textarea").bind('blur', {editor: this}, BestInPlaceEditor.forms.textarea.blurHandler);
|
446
495
|
this.element.find("textarea").bind('keyup', {editor: this}, BestInPlaceEditor.forms.textarea.keyupHandler);
|
@@ -570,7 +619,7 @@ jQuery.fn.best_in_place = function() {
|
|
570
619
|
$twin.appendTo($textarea.parent());
|
571
620
|
|
572
621
|
// Copy the essential styles (mimics) from the textarea to the twin
|
573
|
-
|
622
|
+
i = mimics.length;
|
574
623
|
while(i--){
|
575
624
|
$twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));
|
576
625
|
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
//= require jquery.purr
|
2
|
+
|
3
|
+
$(document).on('best_in_place:error', function(event, request, error) {
|
4
|
+
// Display all error messages from server side validation
|
5
|
+
jQuery.each(jQuery.parseJSON(request.responseText), function(index, value) {
|
6
|
+
if( typeof(value) == "object") {value = index + " " + value.toString(); }
|
7
|
+
var container = jQuery("<span class='flash-error'></span>").html(value);
|
8
|
+
container.purr();
|
9
|
+
});
|
10
|
+
});
|
@@ -6,7 +6,11 @@ module BestInPlace
|
|
6
6
|
|
7
7
|
private
|
8
8
|
def respond_bip_ok(obj)
|
9
|
-
|
9
|
+
if obj.respond_to?(:id)
|
10
|
+
klass = "#{obj.class}_#{obj.id}"
|
11
|
+
else
|
12
|
+
klass = obj.class.to_s
|
13
|
+
end
|
10
14
|
param_key = BestInPlace::Utils.object_to_key(obj)
|
11
15
|
updating_attr = params[param_key].keys.first
|
12
16
|
|
@@ -14,6 +14,8 @@ module BestInPlace
|
|
14
14
|
BestInPlace::ViewHelpers.send(opts[:method], object.send(opts[:attr]))
|
15
15
|
end
|
16
16
|
{:display_as => value}.to_json
|
17
|
+
when :proc
|
18
|
+
{:display_as => opts[:proc].call(object.send(opts[:attr]))}.to_json
|
17
19
|
else
|
18
20
|
{}.to_json
|
19
21
|
end
|
@@ -34,5 +36,9 @@ module BestInPlace
|
|
34
36
|
def add_helper_method(klass, attr, helper_method, helper_options = nil)
|
35
37
|
@@table[klass.to_s][attr.to_s] = Renderer.new :method => helper_method.to_sym, :type => :helper, :attr => attr, :helper_options => helper_options
|
36
38
|
end
|
39
|
+
|
40
|
+
def add_helper_proc(klass, attr, helper_proc)
|
41
|
+
@@table[klass.to_s][attr.to_s] = Renderer.new :type => :proc, :attr => attr, :proc => helper_proc
|
42
|
+
end
|
37
43
|
end
|
38
44
|
end
|
data/lib/best_in_place/helper.rb
CHANGED
@@ -10,7 +10,7 @@ module BestInPlace
|
|
10
10
|
raise ArgumentError, "Can't find helper #{opts[:display_with]}"
|
11
11
|
end
|
12
12
|
|
13
|
-
real_object =
|
13
|
+
real_object = real_object_for object
|
14
14
|
opts[:type] ||= :input
|
15
15
|
opts[:collection] ||= []
|
16
16
|
field = field.to_s
|
@@ -20,7 +20,7 @@ module BestInPlace
|
|
20
20
|
collection = nil
|
21
21
|
if opts[:type] == :select && !opts[:collection].blank?
|
22
22
|
v = real_object.send(field)
|
23
|
-
value = Hash[opts[:collection]][
|
23
|
+
value = Hash[opts[:collection]].stringify_keys[v.to_s]
|
24
24
|
collection = opts[:collection].to_json
|
25
25
|
end
|
26
26
|
if opts[:type] == :checkbox
|
@@ -31,7 +31,13 @@ module BestInPlace
|
|
31
31
|
value = fieldValue ? opts[:collection][1] : opts[:collection][0]
|
32
32
|
collection = opts[:collection].to_json
|
33
33
|
end
|
34
|
-
|
34
|
+
classes = ["best_in_place"]
|
35
|
+
unless opts[:classes].nil?
|
36
|
+
# the next three lines enable this opt to handle both a stings and a arrays
|
37
|
+
classes << opts[:classes]
|
38
|
+
classes.flatten!
|
39
|
+
end
|
40
|
+
out = "<span class='#{classes.join(" ")}'"
|
35
41
|
out << " id='#{BestInPlace::Utils.build_best_in_place_id(real_object, field)}'"
|
36
42
|
out << " data-url='#{opts[:path].blank? ? url_for(object) : url_for(opts[:path])}'"
|
37
43
|
out << " data-object='#{opts[:object_name] || BestInPlace::Utils.object_to_key(real_object)}'"
|
@@ -41,6 +47,7 @@ module BestInPlace
|
|
41
47
|
out << " data-ok-button='#{opts[:ok_button]}'" unless opts[:ok_button].blank?
|
42
48
|
out << " data-cancel-button='#{opts[:cancel_button]}'" unless opts[:cancel_button].blank?
|
43
49
|
out << " data-nil='#{opts[:nil]}'" unless opts[:nil].blank?
|
50
|
+
out << " data-use-confirm='#{opts[:use_confirm]}'" unless opts[:use_confirm].nil?
|
44
51
|
out << " data-type='#{opts[:type]}'"
|
45
52
|
out << " data-inner-class='#{opts[:inner_class]}'" if opts[:inner_class]
|
46
53
|
out << " data-html-attrs='#{opts[:html_attrs].to_json}'" unless opts[:html_attrs].blank?
|
@@ -55,9 +62,9 @@ module BestInPlace
|
|
55
62
|
end
|
56
63
|
if !opts[:sanitize].nil? && !opts[:sanitize]
|
57
64
|
out << " data-sanitize='false'>"
|
58
|
-
out << sanitize(value, :tags => %w(b i u s a strong em p h1 h2 h3 h4 h5 ul li ol hr pre span img br), :attributes => %w(id class href))
|
65
|
+
out << sanitize(value.to_s, :tags => %w(b i u s a strong em p h1 h2 h3 h4 h5 ul li ol hr pre span img br), :attributes => %w(id class href))
|
59
66
|
else
|
60
|
-
out << ">#{sanitize(value, :tags => nil, :attributes => nil)}"
|
67
|
+
out << ">#{sanitize(value.to_s, :tags => nil, :attributes => nil)}"
|
61
68
|
end
|
62
69
|
out << "</span>"
|
63
70
|
raw out
|
@@ -67,7 +74,7 @@ module BestInPlace
|
|
67
74
|
if condition
|
68
75
|
best_in_place(object, field, opts)
|
69
76
|
else
|
70
|
-
build_value_for object, field, opts
|
77
|
+
build_value_for real_object_for(object), field, opts
|
71
78
|
end
|
72
79
|
end
|
73
80
|
|
@@ -75,15 +82,20 @@ module BestInPlace
|
|
75
82
|
def build_value_for(object, field, opts)
|
76
83
|
return "" if object.send(field).blank?
|
77
84
|
|
85
|
+
if (object.respond_to?(:id))
|
86
|
+
klass = "#{object.class}_#{object.id}"
|
87
|
+
else
|
88
|
+
klass = object.class.to_s
|
89
|
+
end
|
78
90
|
if opts[:display_as]
|
79
|
-
BestInPlace::DisplayMethods.add_model_method(
|
91
|
+
BestInPlace::DisplayMethods.add_model_method(klass, field, opts[:display_as])
|
80
92
|
object.send(opts[:display_as]).to_s
|
81
93
|
|
82
94
|
elsif opts[:display_with].try(:is_a?, Proc)
|
95
|
+
BestInPlace::DisplayMethods.add_helper_proc(klass, field, opts[:display_with])
|
83
96
|
opts[:display_with].call(object.send(field))
|
84
|
-
|
85
97
|
elsif opts[:display_with]
|
86
|
-
BestInPlace::DisplayMethods.add_helper_method(
|
98
|
+
BestInPlace::DisplayMethods.add_helper_method(klass, field, opts[:display_with], opts[:helper_options])
|
87
99
|
if opts[:helper_options]
|
88
100
|
BestInPlace::ViewHelpers.send(opts[:display_with], object.send(field), opts[:helper_options])
|
89
101
|
else
|
@@ -101,7 +113,11 @@ module BestInPlace
|
|
101
113
|
data.to_s.
|
102
114
|
gsub("&", "&").
|
103
115
|
gsub("'", "'").
|
104
|
-
gsub(
|
116
|
+
gsub(/\r?\n/, " ")
|
117
|
+
end
|
118
|
+
|
119
|
+
def real_object_for(object)
|
120
|
+
(object.is_a?(Array) && object.last.class.respond_to?(:model_name)) ? object.last : object
|
105
121
|
end
|
106
122
|
end
|
107
123
|
end
|
@@ -7,6 +7,7 @@ describe BestInPlace::BestInPlaceHelpers do
|
|
7
7
|
@user = User.new :name => "Lucia",
|
8
8
|
:last_name => "Napoli",
|
9
9
|
:email => "lucianapoli@gmail.com",
|
10
|
+
:height => "5' 5\"",
|
10
11
|
:address => "Via Roma 99",
|
11
12
|
:zip => "25123",
|
12
13
|
:country => "2",
|
@@ -78,6 +79,10 @@ describe BestInPlace::BestInPlaceHelpers do
|
|
78
79
|
@span.attribute("data-cancel-button").should be_nil
|
79
80
|
end
|
80
81
|
|
82
|
+
it "should have no Use-Confirmation dialog option by default" do
|
83
|
+
@span.attribute("data-use-confirm").should be_nil
|
84
|
+
end
|
85
|
+
|
81
86
|
it "should have no inner_class by default" do
|
82
87
|
@span.attribute("data-inner-class").should be_nil
|
83
88
|
end
|
@@ -160,6 +165,13 @@ describe BestInPlace::BestInPlaceHelpers do
|
|
160
165
|
span.attribute("data-cancel-button").value.should == "nasty"
|
161
166
|
end
|
162
167
|
|
168
|
+
it "should have the given Use-Confirmation dialog option" do
|
169
|
+
out = helper.best_in_place @user, :name, :use_confirm => "false"
|
170
|
+
nk = Nokogiri::HTML.parse(out)
|
171
|
+
span = nk.css("span")
|
172
|
+
span.attribute("data-use-confirm").value.should == "false"
|
173
|
+
end
|
174
|
+
|
163
175
|
describe "object_name" do
|
164
176
|
it "should change the data-object value" do
|
165
177
|
out = helper.best_in_place @user, :name, :object_name => "my_user"
|
@@ -213,6 +225,14 @@ describe BestInPlace::BestInPlaceHelpers do
|
|
213
225
|
span.text.should == "º150.00"
|
214
226
|
end
|
215
227
|
end
|
228
|
+
|
229
|
+
describe "array-like objects" do
|
230
|
+
it "should work with array-like objects in order to provide support to namespaces" do
|
231
|
+
nk = Nokogiri::HTML.parse(helper.best_in_place [:admin, @user], :name)
|
232
|
+
span = nk.css("span")
|
233
|
+
span.text.should == "Lucia"
|
234
|
+
end
|
235
|
+
end
|
216
236
|
end
|
217
237
|
|
218
238
|
context "with a text field attribute" do
|
@@ -324,34 +344,58 @@ describe BestInPlace::BestInPlaceHelpers do
|
|
324
344
|
|
325
345
|
describe "#best_in_place_if" do
|
326
346
|
context "when the parameters are valid" do
|
327
|
-
before
|
328
|
-
@
|
329
|
-
|
330
|
-
|
347
|
+
before do
|
348
|
+
@user = User.new :name => "Lucia",
|
349
|
+
:last_name => "Napoli",
|
350
|
+
:email => "lucianapoli@gmail.com",
|
351
|
+
:height => "5' 5\"",
|
352
|
+
:address => "Via Roma 99",
|
353
|
+
:zip => "25123",
|
354
|
+
:country => "2",
|
355
|
+
:receive_email => false,
|
356
|
+
:birth_date => Time.now.utc.to_date,
|
357
|
+
:description => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus a lectus et lacus ultrices auctor. Morbi aliquet convallis tincidunt. Praesent enim libero, iaculis at commodo nec, fermentum a dolor. Quisque eget eros id felis lacinia faucibus feugiat et ante. Aenean justo nisi, aliquam vel egestas vel, porta in ligula. Etiam molestie, lacus eget tincidunt accumsan, elit justo rhoncus urna, nec pretium neque mi et lorem. Aliquam posuere, dolor quis pulvinar luctus, felis dolor tincidunt leo, eget pretium orci purus ac nibh. Ut enim sem, suscipit ac elementum vitae, sodales vel sem.",
|
358
|
+
:money => 150
|
331
359
|
@options = {}
|
332
360
|
end
|
361
|
+
|
333
362
|
context "when the condition is true" do
|
334
363
|
before {@condition = true}
|
364
|
+
|
365
|
+
it "should work with array-like objects in order to provide support to namespaces" do
|
366
|
+
nk = Nokogiri::HTML.parse(helper.best_in_place_if @condition, [:admin, @user], :name)
|
367
|
+
span = nk.css("span")
|
368
|
+
span.text.should == "Lucia"
|
369
|
+
end
|
370
|
+
|
335
371
|
context "when the options parameter is left off" do
|
336
372
|
it "should call best_in_place with the rest of the parameters and empty options" do
|
337
|
-
helper.should_receive(:best_in_place).with(@
|
338
|
-
helper.best_in_place_if @condition, @
|
373
|
+
helper.should_receive(:best_in_place).with(@user, :name, {})
|
374
|
+
helper.best_in_place_if @condition, @user, :name
|
339
375
|
end
|
340
376
|
end
|
377
|
+
|
341
378
|
context "when the options parameter is included" do
|
342
379
|
it "should call best_in_place with the rest of the parameters" do
|
343
|
-
helper.should_receive(:best_in_place).with(@
|
344
|
-
helper.best_in_place_if @condition, @
|
380
|
+
helper.should_receive(:best_in_place).with(@user, :name, @options)
|
381
|
+
helper.best_in_place_if @condition, @user, :name, @options
|
345
382
|
end
|
346
383
|
end
|
347
384
|
end
|
385
|
+
|
348
386
|
context "when the condition is false" do
|
349
387
|
before {@condition = false}
|
388
|
+
|
389
|
+
it "should work with array-like objects in order to provide support to namespaces" do
|
390
|
+
helper.best_in_place_if(@condition, [:admin, @user], :name).should eq "Lucia"
|
391
|
+
end
|
392
|
+
|
350
393
|
it "should return the value of the field when the options value is left off" do
|
351
|
-
helper.best_in_place_if(@condition, @
|
394
|
+
helper.best_in_place_if(@condition, @user, :name).should eq "Lucia"
|
352
395
|
end
|
396
|
+
|
353
397
|
it "should return the value of the field when the options value is included" do
|
354
|
-
helper.best_in_place_if(@condition, @
|
398
|
+
helper.best_in_place_if(@condition, @user, :name, @options).should eq "Lucia"
|
355
399
|
end
|
356
400
|
end
|
357
401
|
end
|
data/spec/integration/js_spec.rb
CHANGED
@@ -6,6 +6,7 @@ describe "JS behaviour", :js => true do
|
|
6
6
|
@user = User.new :name => "Lucia",
|
7
7
|
:last_name => "Napoli",
|
8
8
|
:email => "lucianapoli@gmail.com",
|
9
|
+
:height => "5' 5\"",
|
9
10
|
:address => "Via Roma 99",
|
10
11
|
:zip => "25123",
|
11
12
|
:country => "2",
|
@@ -13,9 +14,11 @@ describe "JS behaviour", :js => true do
|
|
13
14
|
:birth_date => Time.now.utc,
|
14
15
|
:description => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus a lectus et lacus ultrices auctor. Morbi aliquet convallis tincidunt. Praesent enim libero, iaculis at commodo nec, fermentum a dolor. Quisque eget eros id felis lacinia faucibus feugiat et ante. Aenean justo nisi, aliquam vel egestas vel, porta in ligula. Etiam molestie, lacus eget tincidunt accumsan, elit justo rhoncus urna, nec pretium neque mi et lorem. Aliquam posuere, dolor quis pulvinar luctus, felis dolor tincidunt leo, eget pretium orci purus ac nibh. Ut enim sem, suscipit ac elementum vitae, sodales vel sem.",
|
15
16
|
:money => 100,
|
17
|
+
:money_proc => 100,
|
16
18
|
:favorite_color => 'Red',
|
17
19
|
:favorite_books => "The City of Gold and Lead",
|
18
|
-
:description => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus a lectus et lacus ultrices auctor. Morbi aliquet convallis tincidunt. Praesent enim libero, iaculis at commodo nec, fermentum a dolor. Quisque eget eros id felis lacinia faucibus feugiat et ante. Aenean justo nisi, aliquam vel egestas vel, porta in ligula. Etiam molestie, lacus eget tincidunt accumsan, elit justo rhoncus urna, nec pretium neque mi et lorem. Aliquam posuere, dolor quis pulvinar luctus, felis dolor tincidunt leo, eget pretium orci purus ac nibh. Ut enim sem, suscipit ac elementum vitae, sodales vel sem."
|
20
|
+
:description => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus a lectus et lacus ultrices auctor. Morbi aliquet convallis tincidunt. Praesent enim libero, iaculis at commodo nec, fermentum a dolor. Quisque eget eros id felis lacinia faucibus feugiat et ante. Aenean justo nisi, aliquam vel egestas vel, porta in ligula. Etiam molestie, lacus eget tincidunt accumsan, elit justo rhoncus urna, nec pretium neque mi et lorem. Aliquam posuere, dolor quis pulvinar luctus, felis dolor tincidunt leo, eget pretium orci purus ac nibh. Ut enim sem, suscipit ac elementum vitae, sodales vel sem.",
|
21
|
+
:favorite_movie => "The Hitchhiker's Guide to the Galaxy"
|
19
22
|
end
|
20
23
|
|
21
24
|
describe "namespaced controllers" do
|
@@ -64,6 +67,44 @@ describe "JS behaviour", :js => true do
|
|
64
67
|
end
|
65
68
|
end
|
66
69
|
|
70
|
+
it "should be able to update last but one item in list" do
|
71
|
+
@user.save!
|
72
|
+
@user2 = User.create :name => "Test",
|
73
|
+
:last_name => "User",
|
74
|
+
:email => "test@example.com",
|
75
|
+
:height => "5' 5\"",
|
76
|
+
:address => "Via Roma 99",
|
77
|
+
:zip => "25123",
|
78
|
+
:country => "2",
|
79
|
+
:receive_email => false,
|
80
|
+
:birth_date => Time.now.utc,
|
81
|
+
:description => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus a lectus et lacus ultrices auctor. Morbi aliquet convallis tincidunt. Praesent enim libero, iaculis at commodo nec, fermentum a dolor. Quisque eget eros id felis lacinia faucibus feugiat et ante. Aenean justo nisi, aliquam vel egestas vel, porta in ligula. Etiam molestie, lacus eget tincidunt accumsan, elit justo rhoncus urna, nec pretium neque mi et lorem. Aliquam posuere, dolor quis pulvinar luctus, felis dolor tincidunt leo, eget pretium orci purus ac nibh. Ut enim sem, suscipit ac elementum vitae, sodales vel sem.",
|
82
|
+
:money => 100,
|
83
|
+
:money_proc => 100,
|
84
|
+
:favorite_color => 'Red',
|
85
|
+
:favorite_books => "The City of Gold and Lead",
|
86
|
+
:description => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus a lectus et lacus ultrices auctor. Morbi aliquet convallis tincidunt. Praesent enim libero, iaculis at commodo nec, fermentum a dolor. Quisque eget eros id felis lacinia faucibus feugiat et ante. Aenean justo nisi, aliquam vel egestas vel, porta in ligula. Etiam molestie, lacus eget tincidunt accumsan, elit justo rhoncus urna, nec pretium neque mi et lorem. Aliquam posuere, dolor quis pulvinar luctus, felis dolor tincidunt leo, eget pretium orci purus ac nibh. Ut enim sem, suscipit ac elementum vitae, sodales vel sem."
|
87
|
+
|
88
|
+
visit users_path
|
89
|
+
within("tr#user_#{@user.id} > .name > span") do
|
90
|
+
page.should have_content("Lucia")
|
91
|
+
page.should have_xpath("//a[contains(@href,'#{user_path(@user)}')]")
|
92
|
+
end
|
93
|
+
|
94
|
+
id = BestInPlace::Utils.build_best_in_place_id @user, :name
|
95
|
+
page.execute_script <<-JS
|
96
|
+
$("#edit_#{@user.id}").click();
|
97
|
+
$("##{id} input[name='name']").val('Lisa');
|
98
|
+
$("##{id} form").submit();
|
99
|
+
JS
|
100
|
+
# binding.pry
|
101
|
+
# visit users_path
|
102
|
+
within("tr#user_#{@user.id} > .name > span") do
|
103
|
+
page.should have_content('Lisa')
|
104
|
+
page.should have_xpath("//a[contains(@href,'#{user_path(@user)}')]")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
67
108
|
it "should be able to use bip_text to update a text field" do
|
68
109
|
@user.save!
|
69
110
|
visit user_path(@user)
|
@@ -247,6 +288,23 @@ describe "JS behaviour", :js => true do
|
|
247
288
|
end
|
248
289
|
end
|
249
290
|
|
291
|
+
it "should not ask for confirmation on cancel if it is switched off" do
|
292
|
+
@user.save!
|
293
|
+
visit user_path(@user)
|
294
|
+
|
295
|
+
id = BestInPlace::Utils.build_best_in_place_id @user, :favorite_movie
|
296
|
+
page.execute_script <<-JS
|
297
|
+
$("##{id}").click();
|
298
|
+
$("##{id} input[name='favorite_movie']").val('No good movie');
|
299
|
+
$("##{id} input[type='button']").click();
|
300
|
+
JS
|
301
|
+
|
302
|
+
lambda { page.driver.browser.switch_to.alert }.should raise_exception(Selenium::WebDriver::Error::NoAlertPresentError)
|
303
|
+
within("#favorite_movie") do
|
304
|
+
page.should have_content("The Hitchhiker's Guide to the Galaxy")
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
250
308
|
it "should not submit input on blur if there's an OK button present" do
|
251
309
|
@user.save!
|
252
310
|
visit user_path(@user)
|
@@ -507,6 +565,18 @@ describe "JS behaviour", :js => true do
|
|
507
565
|
end
|
508
566
|
end
|
509
567
|
|
568
|
+
it "should let me use custom helpers with a lambda" do
|
569
|
+
@user.save!
|
570
|
+
visit user_path(@user)
|
571
|
+
|
572
|
+
page.should have_content("100.0 €")
|
573
|
+
bip_text @user, :money_custom, "250"
|
574
|
+
|
575
|
+
within("#money_custom") do
|
576
|
+
page.should have_content("250.0 €")
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
510
580
|
it "should still show the custom format after an error" do
|
511
581
|
@user.save!
|
512
582
|
visit user_path(@user)
|
@@ -579,6 +649,70 @@ describe "JS behaviour", :js => true do
|
|
579
649
|
within("#alt_money") { page.should have_content("€58.00") }
|
580
650
|
end
|
581
651
|
|
652
|
+
describe "display_with using a lambda" do
|
653
|
+
|
654
|
+
|
655
|
+
it "should render the money" do
|
656
|
+
@user.save!
|
657
|
+
visit user_path(@user)
|
658
|
+
|
659
|
+
within("#money_proc") do
|
660
|
+
page.should have_content("$100.00")
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
|
665
|
+
|
666
|
+
it "should show the new value using the helper after a successful update" do
|
667
|
+
@user.save!
|
668
|
+
visit user_path(@user)
|
669
|
+
|
670
|
+
bip_text @user, :money_proc, "240"
|
671
|
+
|
672
|
+
within("#money_proc") do
|
673
|
+
page.should have_content("$240.00")
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
it "should display the original content when editing the form" do
|
678
|
+
@user.save!
|
679
|
+
retry_on_timeout do
|
680
|
+
visit user_path(@user)
|
681
|
+
|
682
|
+
id = BestInPlace::Utils.build_best_in_place_id @user, :money_proc
|
683
|
+
page.execute_script <<-JS
|
684
|
+
$("##{id}").click();
|
685
|
+
JS
|
686
|
+
|
687
|
+
text = page.find("##{id} input").value
|
688
|
+
text.should == "100.0"
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
it "should display the updated content after editing the field two consecutive times" do
|
693
|
+
@user.save!
|
694
|
+
|
695
|
+
retry_on_timeout do
|
696
|
+
visit user_path(@user)
|
697
|
+
|
698
|
+
bip_text @user, :money_proc, "40"
|
699
|
+
|
700
|
+
sleep 1
|
701
|
+
|
702
|
+
id = BestInPlace::Utils.build_best_in_place_id @user, :money_proc
|
703
|
+
page.execute_script <<-JS
|
704
|
+
$("##{id}").click();
|
705
|
+
JS
|
706
|
+
|
707
|
+
sleep 1
|
708
|
+
|
709
|
+
text = page.find("##{id} input").value
|
710
|
+
text.should == "40"
|
711
|
+
end
|
712
|
+
end
|
713
|
+
|
714
|
+
end
|
715
|
+
|
582
716
|
end
|
583
717
|
|
584
718
|
it "should display strings with quotes correctly in fields" do
|
@@ -610,4 +744,42 @@ describe "JS behaviour", :js => true do
|
|
610
744
|
page.should have_link("link in this text", :href => "http://google.es")
|
611
745
|
end
|
612
746
|
end
|
747
|
+
|
748
|
+
it "should display single- and double-quotes in values appropriately" do
|
749
|
+
@user.height = %{5' 6"}
|
750
|
+
@user.save!
|
751
|
+
|
752
|
+
retry_on_timeout do
|
753
|
+
visit user_path(@user)
|
754
|
+
|
755
|
+
id = BestInPlace::Utils.build_best_in_place_id @user, :height
|
756
|
+
page.execute_script <<-JS
|
757
|
+
$("##{id}").click();
|
758
|
+
JS
|
759
|
+
|
760
|
+
page.find("##{id} select").value.should eq(%{5' 6"})
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
764
|
+
it "should save single- and double-quotes in values appropriately" do
|
765
|
+
@user.height = %{5' 10"}
|
766
|
+
@user.save!
|
767
|
+
|
768
|
+
retry_on_timeout do
|
769
|
+
visit user_path(@user)
|
770
|
+
|
771
|
+
id = BestInPlace::Utils.build_best_in_place_id @user, :height
|
772
|
+
page.execute_script <<-JS
|
773
|
+
$("##{id}").click();
|
774
|
+
$("##{id} select").val("5' 7\\\"");
|
775
|
+
$("##{id} select").blur();
|
776
|
+
JS
|
777
|
+
|
778
|
+
sleep 1
|
779
|
+
|
780
|
+
@user.reload
|
781
|
+
@user.height.should eq(%{5' 7"})
|
782
|
+
end
|
783
|
+
end
|
784
|
+
|
613
785
|
end
|
@@ -1,2 +1,29 @@
|
|
1
|
+
#encoding: utf-8
|
1
2
|
module UsersHelper
|
3
|
+
def height_collection
|
4
|
+
[
|
5
|
+
%{5' 1"} ,
|
6
|
+
%{5' 2"} ,
|
7
|
+
%{5' 3"} ,
|
8
|
+
%{5' 4"} ,
|
9
|
+
%{5' 5"} ,
|
10
|
+
%{5' 6"} ,
|
11
|
+
%{5' 7"} ,
|
12
|
+
%{5' 8"} ,
|
13
|
+
%{5' 9"} ,
|
14
|
+
%{5' 10"},
|
15
|
+
%{5' 11"},
|
16
|
+
%{6' 0"} ,
|
17
|
+
%{6' 1"} ,
|
18
|
+
%{6' 2"} ,
|
19
|
+
%{6' 3"} ,
|
20
|
+
%{6' 4"} ,
|
21
|
+
%{6' 5"} ,
|
22
|
+
%{6' 6"}
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
def bb(value)
|
27
|
+
"#{value} €"
|
28
|
+
end
|
2
29
|
end
|
data/test_app/app/models/user.rb
CHANGED
@@ -13,6 +13,9 @@ class User < ActiveRecord::Base
|
|
13
13
|
:format => {:with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message => "has wrong email format"}
|
14
14
|
validates :zip, :numericality => true, :length => { :minimum => 5 }
|
15
15
|
validates_numericality_of :money, :allow_blank => true
|
16
|
+
validates_numericality_of :money_proc, :allow_blank => true
|
17
|
+
|
18
|
+
alias_attribute :money_custom, :money
|
16
19
|
|
17
20
|
def address_format
|
18
21
|
"<b>addr => [#{address}]</b>".html_safe
|
@@ -19,6 +19,10 @@
|
|
19
19
|
<%= f.label :last_name %><br />
|
20
20
|
<%= f.text_field :last_name %>
|
21
21
|
</div>
|
22
|
+
<div class="field">
|
23
|
+
<%= f.label :height %><br />
|
24
|
+
<%= f.select :height, height_collection %>
|
25
|
+
</div>
|
22
26
|
<div class="field">
|
23
27
|
<%= f.label :address %><br />
|
24
28
|
<%= f.text_field :address %>
|
@@ -2,14 +2,20 @@
|
|
2
2
|
|
3
3
|
<table>
|
4
4
|
<tr>
|
5
|
-
<th>Name</th>
|
5
|
+
<th colspan="2">Name</th>
|
6
6
|
<th>Last name</th>
|
7
7
|
<th>Country</th>
|
8
8
|
</tr>
|
9
9
|
|
10
10
|
<% @users.each do |user| %>
|
11
|
-
<tr>
|
12
|
-
<td
|
11
|
+
<tr id="user_<%= user.id %>">
|
12
|
+
<td class="name">
|
13
|
+
<%= best_in_place user, :name,
|
14
|
+
activator: "#edit_#{user.id}",
|
15
|
+
display_with: :link_to,
|
16
|
+
helper_options: user_path(user) %>
|
17
|
+
</td>
|
18
|
+
<td><small><a href="#" id="edit_<%= user.id %>">edit</a></small></td>
|
13
19
|
<td><%= user.last_name %></td>
|
14
20
|
<td><%= COUNTRIES[user.country.to_i] %></td>
|
15
21
|
</tr>
|
@@ -16,6 +16,12 @@
|
|
16
16
|
<%= best_in_place @user, :last_name, :nil => "Nothing to show" %>
|
17
17
|
</td>
|
18
18
|
</tr>
|
19
|
+
<tr>
|
20
|
+
<td>Height</td>
|
21
|
+
<td id="height">
|
22
|
+
<%= best_in_place @user, :height, :type => :select, :collection => height_collection.zip(height_collection), :sanitize => false %>
|
23
|
+
</td>
|
24
|
+
</tr>
|
19
25
|
<tr>
|
20
26
|
<td>Email</td>
|
21
27
|
<td id="email">
|
@@ -86,6 +92,26 @@
|
|
86
92
|
<%= best_in_place @user, :money, :display_with => :number_to_currency %>
|
87
93
|
</td>
|
88
94
|
</tr>
|
95
|
+
<tr>
|
96
|
+
<td>Money with proc</td>
|
97
|
+
<td id="money_proc">
|
98
|
+
<%= best_in_place @user, :money_proc, :display_with => lambda{ |v| number_to_currency(v) } %>
|
99
|
+
</td>
|
100
|
+
</tr>
|
101
|
+
<tr>
|
102
|
+
<td>Money with custom helper</td>
|
103
|
+
<td id="money_custom">
|
104
|
+
<%= best_in_place @user, :money_custom, :display_with => lambda { |x| bb(x) } %>
|
105
|
+
</td>
|
106
|
+
</tr>
|
107
|
+
<tr>
|
108
|
+
<td>Favorite Movie</td>
|
109
|
+
<td id="favorite_movie">
|
110
|
+
<%- opts = { :ok_button => 'Do it!', :cancel_button => 'Nope', :use_confirm => false } %>
|
111
|
+
<%- opts.delete(:ok_button) if params[:suppress_ok_button] %>
|
112
|
+
<%= best_in_place @user, :favorite_movie, opts %>
|
113
|
+
</td>
|
114
|
+
</tr>
|
89
115
|
</table>
|
90
116
|
<br />
|
91
117
|
<hr />
|
data/test_app/db/schema.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended to check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(:version =>
|
14
|
+
ActiveRecord::Schema.define(:version => 20120620165212) do
|
15
15
|
|
16
16
|
create_table "cars", :force => true do |t|
|
17
17
|
t.string "model"
|
@@ -32,6 +32,9 @@ ActiveRecord::Schema.define(:version => 20120513003308) do
|
|
32
32
|
t.text "favorite_books"
|
33
33
|
t.datetime "birth_date"
|
34
34
|
t.float "money"
|
35
|
+
t.float "money_proc"
|
36
|
+
t.string "height"
|
37
|
+
t.string "favorite_movie"
|
35
38
|
end
|
36
39
|
|
37
40
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: best_in_place
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '3.1'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.1'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: jquery-rails
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: rspec-rails
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ~>
|
@@ -43,29 +53,44 @@ dependencies:
|
|
43
53
|
version: 2.8.0
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.8.0
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: nokogiri
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ! '>='
|
53
68
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
69
|
+
version: '0'
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
58
78
|
- !ruby/object:Gem::Dependency
|
59
79
|
name: capybara
|
60
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
61
81
|
none: false
|
62
82
|
requirements:
|
63
|
-
- -
|
83
|
+
- - ~>
|
64
84
|
- !ruby/object:Gem::Version
|
65
|
-
version: 1.
|
85
|
+
version: 1.1.2
|
66
86
|
type: :development
|
67
87
|
prerelease: false
|
68
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.1.2
|
69
94
|
description: BestInPlace is a jQuery script and a Rails 3 helper that provide the
|
70
95
|
method best_in_place to display any object field easily editable for the user by
|
71
96
|
just clicking on it. It supports input data, text data, boolean data and custom
|
@@ -85,6 +110,7 @@ files:
|
|
85
110
|
- Rakefile
|
86
111
|
- best_in_place.gemspec
|
87
112
|
- lib/assets/javascripts/best_in_place.js
|
113
|
+
- lib/assets/javascripts/best_in_place.purr.js
|
88
114
|
- lib/assets/javascripts/jquery.purr.js
|
89
115
|
- lib/best_in_place.rb
|
90
116
|
- lib/best_in_place/check_version.rb
|
@@ -169,6 +195,9 @@ files:
|
|
169
195
|
- test_app/db/migrate/20111217215935_add_birth_date_to_users.rb
|
170
196
|
- test_app/db/migrate/20111224181356_add_money_to_user.rb
|
171
197
|
- test_app/db/migrate/20120513003308_create_cars.rb
|
198
|
+
- test_app/db/migrate/20120607172609_add_favorite_movie_to_users.rb
|
199
|
+
- test_app/db/migrate/20120616170454_add_money_proc_to_users.rb
|
200
|
+
- test_app/db/migrate/20120620165212_add_height_to_user.rb
|
172
201
|
- test_app/db/schema.rb
|
173
202
|
- test_app/db/seeds.rb
|
174
203
|
- test_app/doc/README_FOR_APP
|
@@ -199,15 +228,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
199
228
|
- - ! '>='
|
200
229
|
- !ruby/object:Gem::Version
|
201
230
|
version: '0'
|
231
|
+
segments:
|
232
|
+
- 0
|
233
|
+
hash: 572357039
|
202
234
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
203
235
|
none: false
|
204
236
|
requirements:
|
205
237
|
- - ! '>='
|
206
238
|
- !ruby/object:Gem::Version
|
207
239
|
version: '0'
|
240
|
+
segments:
|
241
|
+
- 0
|
242
|
+
hash: 572357039
|
208
243
|
requirements: []
|
209
244
|
rubyforge_project: best_in_place
|
210
|
-
rubygems_version: 1.8.
|
245
|
+
rubygems_version: 1.8.24
|
211
246
|
signing_key:
|
212
247
|
specification_version: 3
|
213
248
|
summary: It makes any field in place editable by clicking on it, it works for inputs,
|