populate-me 0.14.0 → 0.17.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e07006c1f567c3309a537233c482114dd0536b783c6e82b1cafcf7d5c53fa12
4
- data.tar.gz: f36fc1cb24ac5b59ea7090c6ffc5c425d437ef95e36f528fb012bb721472d185
3
+ metadata.gz: ab01b0340f42771cfda2e496e4dee387c3e098ee2757ee374f5fb9a95fda48b0
4
+ data.tar.gz: b74800276c349db0d4dcc74f46f36d5b6fb6a46cb4d727c5dbfb9e46d35d0aa4
5
5
  SHA512:
6
- metadata.gz: 6e53cfb5b625bbb4835cb5ba5fa5e404dfaac6e23eb607d5f981ff6c60ed67f2939d0dd4a288476ce1bd486804264c6516b2a79439c6ba302a080c2a0270dd2c
7
- data.tar.gz: 2ca83c16c743176e272793b3d411887b7b3ea9a7f4d01ba71cad0fcd2e1417423d526116d7f09ef4f24b4fc85c54b0c9399cb6a6445fb4c074d1ac09892fa1af
6
+ metadata.gz: 7621910d4fec3622e2bad85742c0877fbe6bafdf9dcb28f804e40d5ae255b362de66cdf6d978a1d49fa27874527705d2cec80146e2624f71ac4783a4bd8b5554
7
+ data.tar.gz: c65c5db1c8361cb50ada68e63cc246325590a0e8ca00a2f6a3e90e047f73cbc4a0072293ab52a60317a9b0710e96d7439ac3efd1860d3d134edd848ae144670f
@@ -214,6 +214,14 @@ fieldset {
214
214
  color: #dc322f;
215
215
  }
216
216
 
217
+ .error {
218
+ color: #dc322f;
219
+ }
220
+
221
+ .batch-upload-report {
222
+ margin: 20px;
223
+ }
224
+
217
225
  /* asmSelect */
218
226
 
219
227
  .asmListItem {
@@ -117,13 +117,17 @@ PopulateMe.scroll_to = function(el, column) {
117
117
  // Mark errors for report after form validation.
118
118
  // It adds the .invalid class to invalid fields,
119
119
  // and adds the report after the label of the field.
120
- PopulateMe.mark_errors = function(context,report) {
120
+ PopulateMe.mark_errors = function(context, report, isSubcontext) {
121
+ if (!isSubcontext) {
122
+ $('.invalid', context).removeClass('invalid');
123
+ $('.errors', context).remove();
124
+ }
121
125
  $.each(report,function(k,v) {
122
126
  var field = context.find('> [data-field-name='+k+']:first');
123
127
  if (field.is('fieldset')) {
124
128
  $.each(v, function(index,subreport) {
125
129
  var subcontext = field.find('> .nested-documents > li:nth('+index+')');
126
- PopulateMe.mark_errors(subcontext, subreport);
130
+ PopulateMe.mark_errors(subcontext, subreport, true);
127
131
  });
128
132
  } else {
129
133
  field.addClass('invalid');
@@ -133,6 +137,54 @@ PopulateMe.mark_errors = function(context,report) {
133
137
  });
134
138
  };
135
139
 
140
+ // Navigate columns back
141
+ PopulateMe.navigate_back = function(target_id) {
142
+ // !!! Careful, it only works if we call this from the last column
143
+ var reloader = PopulateMe.finder.find('> li:nth-last-child(3) .selected');
144
+ if (reloader.size() > 0) {
145
+ var reloadee = PopulateMe.finder.find('> li:nth-last-child(2)');
146
+ var current_search = PopulateMe.copy_column_search(reloadee);
147
+ reloader.trigger('click.columnav',[function(cb_object) {
148
+ PopulateMe.restore_column_search(cb_object.column, current_search);
149
+ if (target_id) {
150
+ var target = $('[data-id=' + target_id + ']', cb_object.column);
151
+ if (target.size() > 0) {
152
+ PopulateMe.scroll_to(target, cb_object.column);
153
+ }
154
+ }
155
+ }]);
156
+ } else {
157
+ PopulateMe.finder.trigger('pop.columnav');
158
+ }
159
+ }
160
+
161
+ PopulateMe.fieldMaxSize = function(field) {
162
+ var maxSizeData = field.data('max-size');
163
+ if (!maxSizeData) {
164
+ return null;
165
+ }
166
+ return field.data('max-size');
167
+ };
168
+
169
+ PopulateMe.fieldHasFileTooBig = function(field, maybeFile) {
170
+ var file = maybeFile || field[0].files[0];
171
+ if (!file) {
172
+ return false;
173
+ }
174
+ var maxSize = PopulateMe.fieldMaxSize(field);
175
+ if (!maxSize) {
176
+ return false;
177
+ }
178
+ return file.size > maxSize;
179
+ };
180
+
181
+ PopulateMe.fileTooBigErrorMessage = function(fname, max_size) {
182
+ if (typeof max_size != 'number') {
183
+ max_size = PopulateMe.fieldMaxSize(max_size);
184
+ }
185
+ return 'File too big: ' + fname + ' should be less than ' + PopulateMe.display_file_size(max_size) + '.';
186
+ };
187
+
136
188
  // JS Validations
137
189
  // This adds validations that could happen before sending
138
190
  // anything to the server and that cannot be done with
@@ -146,18 +198,13 @@ PopulateMe.jsValidationsPassed = function(context) {
146
198
  var max_size_fields = $('input[type=file][data-max-size]', context);
147
199
  max_size_fields.each(function() {
148
200
  var field = $(this);
149
- var max_size = parseInt(field.data('max-size'));
150
- if (field[0].files[0]) {
151
- var fsize = field[0].files[0].size;
152
- var fname = field[0].files[0].name;
153
- if (fsize>max_size) {
154
- alert('File too big: '+fname+' should be less than '+PopulateMe.display_file_size(max_size)+'.');
155
- throw "Validation error";
156
- }
201
+ if (PopulateMe.fieldHasFileTooBig(field)) {
202
+ alert(PopulateMe.fileTooBigErrorMessage(file.name, field));
203
+ throw "Validation error";
157
204
  }
158
205
  });
159
206
  } catch(e) {
160
- if (e==='Validation error') {
207
+ if (e==='Validation error') {
161
208
  return false;
162
209
  } else {
163
210
  throw(e);
@@ -167,7 +214,7 @@ PopulateMe.jsValidationsPassed = function(context) {
167
214
  return true;
168
215
  };
169
216
 
170
- // Init column
217
+ // Init column
171
218
  // Bind events and init things that need to happen when
172
219
  // a new column is added to the finder.
173
220
  // The callback `custom_init_column` is also called at the end
@@ -303,48 +350,111 @@ $(function() {
303
350
  });
304
351
 
305
352
  // Ajax form
353
+
354
+ var ajaxSubmitSuccess = function(res) {
355
+ if (res.success == true) {
356
+ PopulateMe.navigate_back(res.data._id);
357
+ }
358
+ };
359
+
360
+ var ajaxSubmitError = function(xhr, ctx) {
361
+ res = xhr.responseJSON;
362
+ if (res.success == false) {
363
+ PopulateMe.mark_errors(ctx, res.data);
364
+ PopulateMe.scroll_to(ctx.find('.invalid:first'));
365
+ $('input[type=submit]', ctx).show();
366
+ ctx.fadeTo("fast", 1);
367
+ }
368
+ };
369
+
306
370
  $('body').on('submit','form.admin-post, form.admin-put', function(e) {
307
371
  e.preventDefault();
308
372
  var self = $(this);
309
- if (PopulateMe.jsValidationsPassed(self)) {
310
-
311
- var submit_button = $('input[type=submit]',self);
312
- submit_button.hide();
313
- $.ajax({
314
- url: self.attr('action'),
315
- type: (self.is('.admin-put') ? 'put' : 'post'),
316
- data: new FormData(this),
317
- processData: false,
318
- contentType: false,
319
- success: function(res) {
320
- if (res.success==true) {
321
- var reloader = PopulateMe.finder.find('> li:nth-last-child(3) .selected');
322
- if (reloader.size()>0) {
323
- var reloadee = PopulateMe.finder.find('> li:nth-last-child(2)');
324
- var current_search = PopulateMe.copy_column_search(reloadee);
325
- reloader.trigger('click.columnav',[function(cb_object) {
326
- var target = $('[data-id='+res.data._id+']', cb_object.column);
327
- if (target.size()>0) {
328
- PopulateMe.restore_column_search(cb_object.column, current_search);
329
- PopulateMe.scroll_to(target, cb_object.column);
330
- }
331
- }]);
332
- } else {
333
- PopulateMe.finder.trigger('pop.columnav');
334
- }
373
+ self.fadeTo("fast", 0.3);
374
+ var submit_button = $('input[type=submit]', self);
375
+ var formData = new FormData(this);
376
+ var batchField = self.data('batch-field');
377
+ var batchFieldEl = $("input[name='" + batchField + "']");
378
+ var isBatchUpload = self.is('.admin-post') &&
379
+ batchField &&
380
+ formData.getAll(batchField).length > 1;
381
+
382
+ if (isBatchUpload) {
383
+
384
+ if (confirm("You've selected multiple images. This will create an entry for each image. It may take a while to upload everything. Do you wish to proceed ?")) {
385
+ submit_button.hide();
386
+ var files = formData.getAll(batchField);
387
+ var report = $("<div class='batch-upload-report'></div>").insertAfter(self);
388
+ var latestSuccessfulId;
389
+ var errorSize = 0;
390
+ var successSize = 0;
391
+ var addCloseButtonIfDone = function() {
392
+ if (errorSize + successSize >= files.length) {
393
+ var closeButton = $("<button type='button'>Close</button>").click(function(e) {
394
+ e.preventDefault();
395
+ PopulateMe.navigate_back();
396
+ });
397
+ report.append(closeButton);
335
398
  }
336
- },
337
- error: function(xhr) {
338
- res = xhr.responseJSON;
339
- if (res.success==false) {
340
- $('.invalid',self).removeClass('invalid');
341
- $('.errors',self).remove();
342
- PopulateMe.mark_errors(self,res.data);
343
- PopulateMe.scroll_to(self.find('.invalid:first'));
344
- submit_button.show();
399
+ };
400
+ for (var i = 0; i < files.length; i++) {
401
+ var file = files[i];
402
+ if (PopulateMe.fieldHasFileTooBig(batchFieldEl, file)) {
403
+ errorSize += 1;
404
+ var msg = PopulateMe.fileTooBigErrorMessage(file.name, batchFieldEl);
405
+ report.append("<div class='error'>" + msg + "</div>");
406
+ addCloseButtonIfDone();
407
+ } else {
408
+ formData.set(batchField, file, file.name);
409
+ $.ajax({
410
+ url: self.attr('action'),
411
+ type: (self.is('.admin-put') ? 'put' : 'post'), // Always post ?
412
+ data: formData,
413
+ successData: {filename: file.name, index: i+1},
414
+ processData: false,
415
+ contentType: false,
416
+ success: function(res, textStatus, xhr) {
417
+ successSize += 1;
418
+ if (res.success == true) {
419
+ latestSuccessfulId = res.data._id;
420
+ report.append("<div>Uploaded: " + this.successData.filename + "</div>");
421
+ }
422
+ if (successSize >= files.length) {
423
+ PopulateMe.navigate_back(res.data._id);
424
+ } else {
425
+ addCloseButtonIfDone();
426
+ }
427
+ },
428
+ error: function(xhr, textStatus, errorThrown) {
429
+ errorSize += 1;
430
+ report.append("<div class='error'>Error: " + this.successData.filename + "</div>");
431
+ res = xhr.responseJSON;
432
+ if (res.success == false) {
433
+ PopulateMe.mark_errors(self, res.data);
434
+ }
435
+ addCloseButtonIfDone();
436
+ }
437
+ });
345
438
  }
346
439
  }
347
- });
440
+ }
441
+
442
+ } else {
443
+
444
+ if (PopulateMe.jsValidationsPassed(self)) {
445
+ submit_button.hide();
446
+ $.ajax({
447
+ url: self.attr('action'),
448
+ type: (self.is('.admin-put') ? 'put' : 'post'),
449
+ data: formData,
450
+ processData: false,
451
+ contentType: false,
452
+ success: ajaxSubmitSuccess,
453
+ error: function(xhr, textStatus, errorThrown) {
454
+ ajaxSubmitError(xhr, self);
455
+ }
456
+ });
457
+ }
348
458
 
349
459
  }
350
460
  });
@@ -4,6 +4,7 @@
4
4
  <head>
5
5
  <meta charset="utf-8" />
6
6
  <title><%= settings.meta_title %></title>
7
+ <link href="<%= request.script_name %>/__assets__/img/favicon.png" rel="icon" type="image/png">
7
8
  <link rel="stylesheet" href="<%= request.script_name %>/__assets__/css/jquery-ui.min.css" type="text/css" media='screen' />
8
9
  <link rel="stylesheet" href="<%= request.script_name %>/__assets__/css/asmselect.css" type="text/css" media='screen' />
9
10
  <link rel="stylesheet" href="<%= request.script_name %>/__assets__/css/main.css" type="text/css" media='screen' />
@@ -79,7 +80,7 @@
79
80
  {{#polymorphic_type}}
80
81
  <p>({{polymorphic_type}})</p>
81
82
  {{/polymorphic_type}}
82
- <form action="<%= request.script_name %>/api/{{admin_url}}" method="POST" accept-charset="utf-8" class='admin-{{#is_new}}post{{/is_new}}{{^is_new}}put{{/is_new}}'>
83
+ <form action="<%= request.script_name %>/api/{{admin_url}}" method="POST" accept-charset="utf-8" class='admin-{{#is_new}}post{{/is_new}}{{^is_new}}put{{/is_new}}' {{#batch_field}}{{#is_new}}data-batch-field="{{batch_field}}"{{/is_new}}{{/batch_field}}>
83
84
  {{#custom_partial_or_default}}template_form_fields{{/custom_partial_or_default}}
84
85
  {{^is_new}}
85
86
  <input type="hidden" name="_method" value="PUT" />
@@ -117,7 +118,14 @@
117
118
  </script>
118
119
 
119
120
  <script id="template-string-field" type="x-tmpl-mustache">
120
- <input name='{{input_name}}' value='{{input_value}}' {{#required}}required{{/required}}{{{build_input_attributes}}} />
121
+ <input name='{{input_name}}' value='{{input_value}}' {{#required}}required{{/required}}{{{build_input_attributes}}} {{#autocomplete.length}}list='datalist-{{id}}'{{/autocomplete.length}} />
122
+ {{#autocomplete.length}}
123
+ <datalist id='datalist-{{id}}'>
124
+ {{#autocomplete}}
125
+ <option value='{{.}}' />
126
+ {{/autocomplete}}
127
+ </datalist>
128
+ {{/autocomplete.length}}
121
129
  </script>
122
130
 
123
131
  <script id="template-text-field" type="x-tmpl-mustache">
@@ -149,7 +157,7 @@
149
157
  <button class='attachment-deleter'>x</button>
150
158
  <br />
151
159
  {{/url}}
152
- <input type='file' name='{{input_name}}' {{#max_size}}data-max-size='{{max_size}}'{{/max_size}} {{{build_input_atrributes}}} />
160
+ <input type='file' name='{{input_name}}' {{#multiple}}multiple{{/multiple}} {{#max_size}}data-max-size='{{max_size}}'{{/max_size}} {{{build_input_atrributes}}} />
153
161
  </script>
154
162
 
155
163
  <script id="template-list-field" type="x-tmpl-mustache">
@@ -123,11 +123,17 @@ class PopulateMe::Admin < Sinatra::Base
123
123
  # Method = overridable = testable
124
124
  ENV['CERBERUS_PASS']
125
125
  end
126
+
126
127
  def cerberus_available?
127
128
  # Method = overridable = testable
128
129
  Rack.const_defined?(:Cerberus)
129
130
  end
130
131
 
132
+ def cerberus_auth user, pass, req
133
+ pass == cerberus_pass
134
+ end
135
+
136
+
131
137
  private
132
138
 
133
139
  def setup_default_middleware builder
@@ -151,8 +157,8 @@ class PopulateMe::Admin < Sinatra::Base
151
157
  return unless settings.cerberus_active
152
158
  cerberus_settings = settings.cerberus==true ? {} : settings.cerberus
153
159
  cerberus_settings[:session_key] = 'populate_me_user'
154
- builder.use Rack::Cerberus, cerberus_settings do |user,pass,req|
155
- pass==cerberus_pass
160
+ builder.use Rack::Cerberus, cerberus_settings do |user, pass, req|
161
+ cerberus_auth user, pass, req
156
162
  end
157
163
  end
158
164
 
@@ -27,7 +27,7 @@ module PopulateMe
27
27
  # It can be used on its own but it keeps everything
28
28
  # in memory. Which means it is only for tests and conceptual
29
29
  # understanding.
30
-
30
+
31
31
  include DocumentMixins::Typecasting
32
32
  include DocumentMixins::Outcasting
33
33
  include DocumentMixins::Schema
@@ -48,12 +48,16 @@ module PopulateMe
48
48
  end
49
49
  page_title = self.new? ? "New #{self.class.to_s_short}" : self.to_s
50
50
  # page_title << " (#{self.polymorphic_type})" if self.class.polymorphic?
51
+ batch_field_item = items.find do |item|
52
+ item[:field_name] == self.class.batch_field
53
+ end
51
54
  {
52
55
  template: "template#{'_nested' if o[:nested]}_form",
53
56
  page_title: page_title,
54
57
  admin_url: self.to_admin_url,
55
58
  is_new: self.new?,
56
59
  polymorphic_type: self.class.polymorphic? ? self.polymorphic_type : nil,
60
+ batch_field: (not self.new? or batch_field_item.nil?) ? nil : batch_field_item[:input_name],
57
61
  fields: items
58
62
  }
59
63
  end
@@ -21,6 +21,14 @@ module PopulateMe
21
21
  end
22
22
  end
23
23
 
24
+ def outcast_string field, item, o={}
25
+ if item.key? :autocomplete
26
+ item = item.dup
27
+ item[:autocomplete] = WebUtils.deep_copy(WebUtils.get_value(item[:autocomplete],self))
28
+ end
29
+ item
30
+ end
31
+
24
32
  def outcast_list field, item, o={}
25
33
  item = item.dup
26
34
  item[:items] = self.__send__(field).map do |nested|
@@ -66,6 +74,7 @@ module PopulateMe
66
74
  def outcast_attachment field, item, o={}
67
75
  item = item.dup
68
76
  item[:url] = self.attachment(field).url
77
+ item[:multiple] = (self.new? and self.class.batch_field == field)
69
78
  item
70
79
  end
71
80
 
@@ -118,14 +118,21 @@ module PopulateMe
118
118
  def label sym # sets the label_field
119
119
  @label_field = sym.to_sym
120
120
  end
121
-
121
+
122
122
  def label_field
123
123
  return @label_field if self.fields.empty?
124
- @label_field || self.fields.find do |k,v|
124
+ @label_field || self.fields.find do |k,v|
125
125
  not [:id,:polymorphic_type].include?(v[:type])
126
126
  end[0]
127
127
  end
128
128
 
129
+ def batch_on_field sym # sets the batch_field
130
+ @batch_field = sym.to_sym
131
+ end
132
+ def batch_field
133
+ @batch_field
134
+ end
135
+
129
136
  def sort_by f, direction=:asc
130
137
  raise(ArgumentError) unless [:asc,:desc].include? direction
131
138
  raise(ArgumentError) unless self.new.respond_to? f
@@ -1,4 +1,4 @@
1
1
  module PopulateMe
2
- VERSION = '0.14.0'
2
+ VERSION = '0.17.0'
3
3
  end
4
4
 
data/populate-me.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
21
21
  s.add_dependency 'sinatra', '~> 2'
22
22
  s.add_dependency 'json', '~> 2.1'
23
23
 
24
- s.add_development_dependency 'bundler', '~> 1.13'
24
+ s.add_development_dependency 'bundler', '>= 2.2.10'
25
25
  s.add_development_dependency 'minitest', '~> 5.8'
26
26
  s.add_development_dependency 'rack-test', '~> 0.6'
27
27
  s.add_development_dependency 'rack-cerberus', '~> 1.0'
data/test/test_admin.rb CHANGED
@@ -22,6 +22,12 @@ class AdminWithCerberusPass < Admin
22
22
  end
23
23
  end
24
24
 
25
+ class AdminWithCustomCerberusAuth < AdminWithCerberusPass
26
+ def self.cerberus_auth user, pass, req
27
+ [user, pass] == ['mario', '1234']
28
+ end
29
+ end
30
+
25
31
  class AdminCerberusNotAvailable < AdminWithCerberusPass
26
32
  def self.cerberus_available?
27
33
  false
@@ -106,19 +112,54 @@ describe PopulateMe::Admin do
106
112
  assert_equal 'text/css', last_response.content_type
107
113
  end
108
114
 
109
- describe 'when cerberus is active' do
115
+ describe 'When cerberus is active' do
110
116
  let(:app) { AdminWithCerberusPass.new }
111
117
  it 'Uses Cerberus for authentication' do
112
118
  get '/'
113
119
  assert_equal 401, last_response.status
114
120
  end
121
+ it 'Authenticates with right login details' do
122
+ get '/', {
123
+ cerberus_login: 'admin',
124
+ cerberus_pass: '123',
125
+ _method: 'get'
126
+ }
127
+ assert_equal 200, last_response.status
128
+ end
129
+ it 'Fails authentication when login details are wrong' do
130
+ get '/', {
131
+ cerberus_login: 'admin',
132
+ cerberus_pass: 'xxx',
133
+ _method: 'get'
134
+ }
135
+ assert_equal 401, last_response.status
136
+ end
115
137
  end
116
- describe 'when cerberus is inactive' do
138
+ describe 'When cerberus is inactive' do
117
139
  it 'Does not use Cerberus' do
118
140
  get '/'
119
141
  assert_predicate last_response, :ok?
120
142
  end
121
143
  end
144
+ describe 'When cerberus_auth is overridden' do
145
+ let(:app) { AdminWithCustomCerberusAuth.new }
146
+ it 'Authenticates with right login details' do
147
+ get '/', {
148
+ cerberus_login: 'mario',
149
+ cerberus_pass: '1234',
150
+ _method: 'get'
151
+ }
152
+ assert_equal 200, last_response.status
153
+ end
154
+ it 'Fails authentication when login details are wrong' do
155
+ get '/', {
156
+ cerberus_login: 'admin',
157
+ cerberus_pass: '123',
158
+ _method: 'get'
159
+ }
160
+ assert_equal 401, last_response.status
161
+ end
162
+ end
122
163
 
123
164
  end
124
165
 
@@ -178,7 +178,7 @@ describe PopulateMe::Document, 'AdminAdapter' do
178
178
  describe '#to_admin_form' do
179
179
  class PolyForm < PopulateMe::Document
180
180
  field :name
181
- field :image, only_for: 'Image'
181
+ field :image, only_for: 'Image', type: :attachment, class_name: PopulateMe::Attachment
182
182
  field :title, only_for: 'Article'
183
183
  field :content, only_for: 'Article'
184
184
  field :position
@@ -215,6 +215,29 @@ describe PopulateMe::Document, 'AdminAdapter' do
215
215
  form = obj.to_admin_form
216
216
  assert_nil form[:polymorphic_type]
217
217
  end
218
+
219
+ class WithBatchField < PopulateMe::Document
220
+ field :name
221
+ field :thumbnail, type: :attachment, class_name: PopulateMe::Attachment
222
+ batch_on_field :thumbnail
223
+ end
224
+ it 'Sets :batch_field if there is one' do
225
+ obj = PolyForm.new
226
+ form = obj.to_admin_form
227
+ assert_nil form[:batch_field]
228
+ obj = NotPolyForm.new
229
+ form = obj.to_admin_form
230
+ assert_nil form[:batch_field]
231
+ obj = WithBatchField.new
232
+ form = obj.to_admin_form
233
+ assert_equal 'data[thumbnail]', form[:batch_field]
234
+ end
235
+ it 'Does not add a batch field when updating' do
236
+ obj = WithBatchField.new
237
+ obj._is_new = false
238
+ form = obj.to_admin_form
239
+ assert_nil form[:batch_field]
240
+ end
218
241
  end
219
242
 
220
243
  end
@@ -6,6 +6,7 @@ class Outcasted < PopulateMe::Document
6
6
  set :default_attachment_class, PopulateMe::Attachment
7
7
 
8
8
  field :name
9
+ field :category, autocomplete: ['Fish', 'Cat', 'Bunny']
9
10
  field :size, type: :select, select_options: [
10
11
  {description: 'small', value: 's'},
11
12
  {description: 'medium', value: 'm'},
@@ -19,10 +20,17 @@ class Outcasted < PopulateMe::Document
19
20
  field :tags, type: :select, select_options: ['art','sport','science'], multiple: true
20
21
  field :related_properties, type: :select, select_options: ['prop1','prop2','prop3'], multiple: true
21
22
  field :pdf, type: :attachment
23
+ field :image, type: :attachment
22
24
  field :authors, type: :list
23
25
  field :weirdo, type: :strange
24
26
  field :price, type: :price
25
27
 
28
+ batch_on_field :image
29
+
30
+ def get_category_autocomplete_list
31
+ ['Horse', 'Bear']
32
+ end
33
+
26
34
  def get_size_options
27
35
  [
28
36
  [:small, :s],
@@ -37,6 +45,11 @@ class Outcasted::Author < PopulateMe::Document
37
45
  field :name
38
46
  end
39
47
 
48
+ class OutcastedNoBatchField < PopulateMe::Document
49
+ set :default_attachment_class, PopulateMe::Attachment
50
+ field :image, type: :attachment
51
+ end
52
+
40
53
  describe PopulateMe::Document, 'Outcasting' do
41
54
 
42
55
  parallelize_me!
@@ -65,6 +78,36 @@ describe PopulateMe::Document, 'Outcasting' do
65
78
 
66
79
  end
67
80
 
81
+ describe '#outcast_string' do
82
+
83
+ it 'Generates the autocomplete options when needed' do
84
+ original = Outcasted.fields[:category]
85
+ output = Outcasted.new.outcast(:category, original, {input_name_prefix: 'data'})
86
+ assert_equal ['Fish', 'Cat', 'Bunny'], output[:autocomplete]
87
+ refute original.equal?(output)
88
+
89
+ original = Outcasted.fields[:name]
90
+ output = Outcasted.new.outcast(:name, original, {input_name_prefix: 'data'})
91
+ refute output.key?(:autocomplete)
92
+ end
93
+
94
+ it 'Generates the autocomplete options from a proc' do
95
+ original = Outcasted.fields[:category].dup
96
+ original[:autocomplete] = proc{ ['Dog', 'Snake'] }
97
+ output = Outcasted.new.outcast(:category, original, {input_name_prefix: 'data'})
98
+ assert_equal ['Dog', 'Snake'], output[:autocomplete]
99
+ assert original[:autocomplete].is_a?(Proc)
100
+ end
101
+
102
+ it 'Generates the autocomplete options from a method name' do
103
+ original = Outcasted.fields[:category].dup
104
+ original[:autocomplete] = :get_category_autocomplete_list
105
+ output = Outcasted.new.outcast(:category, original, {input_name_prefix: 'data'})
106
+ assert_equal ['Horse', 'Bear'], output[:autocomplete]
107
+ assert original[:autocomplete].is_a?(Symbol)
108
+ end
109
+ end
110
+
68
111
  describe '#outcast_list' do
69
112
 
70
113
  it 'Has no value and an empty list of items when list is empty' do
@@ -241,6 +284,35 @@ describe PopulateMe::Document, 'Outcasting' do
241
284
  assert_equal outcasted.attachment(:pdf).url, output[:url]
242
285
  end
243
286
 
287
+ it 'Sets multiple if field is the batch field and document is new' do
288
+ original = Outcasted.fields[:image]
289
+ outcasted = Outcasted.new
290
+ output = outcasted.outcast(:image, original, {input_name_prefix: 'data'})
291
+ assert output[:multiple]
292
+ end
293
+
294
+ it 'Does not set multiple if there is no batch field' do
295
+ original = OutcastedNoBatchField.fields[:image]
296
+ outcasted = OutcastedNoBatchField.new
297
+ output = outcasted.outcast(:image, original, {input_name_prefix: 'data'})
298
+ refute output[:multiple]
299
+ end
300
+
301
+ it 'Does not set multiple if document is not new' do
302
+ original = Outcasted.fields[:image]
303
+ outcasted = Outcasted.new
304
+ outcasted._is_new = false
305
+ output = outcasted.outcast(:image, original, {input_name_prefix: 'data'})
306
+ refute output[:multiple]
307
+ end
308
+
309
+ it 'Does not set multiple if field not the batch field' do
310
+ original = Outcasted.fields[:pdf]
311
+ outcasted = Outcasted.new
312
+ output = outcasted.outcast(:pdf, original, {input_name_prefix: 'data'})
313
+ refute output[:multiple]
314
+ end
315
+
244
316
  end
245
317
 
246
318
  end
@@ -6,6 +6,26 @@ describe PopulateMe::Document, 'Schema' do
6
6
 
7
7
  parallelize_me!
8
8
 
9
+ describe "Batch field" do
10
+
11
+ class BatchFieldSet < PopulateMe::Document
12
+ field :name
13
+ field :image
14
+ batch_on_field :image
15
+ end
16
+
17
+ class NotBatchFieldSet < PopulateMe::Document
18
+ field :name
19
+ field :image
20
+ end
21
+
22
+ it 'Sets batch field correctly' do
23
+ assert_equal :image, BatchFieldSet.batch_field
24
+ assert_nil NotBatchFieldSet.batch_field
25
+ end
26
+
27
+ end
28
+
9
29
  describe "Relationships" do
10
30
 
11
31
  class Relative < PopulateMe::Document
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: populate-me
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mickael Riga
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-03 00:00:00.000000000 Z
11
+ date: 2022-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: web-utils
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '1.13'
61
+ version: 2.2.10
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '1.13'
68
+ version: 2.2.10
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: minitest
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -198,6 +198,7 @@ files:
198
198
  - lib/populate_me/admin/__assets__/css/asmselect.css
199
199
  - lib/populate_me/admin/__assets__/css/jquery-ui.min.css
200
200
  - lib/populate_me/admin/__assets__/css/main.css
201
+ - lib/populate_me/admin/__assets__/img/favicon.png
201
202
  - lib/populate_me/admin/__assets__/img/file.png
202
203
  - lib/populate_me/admin/__assets__/img/help/children.png
203
204
  - lib/populate_me/admin/__assets__/img/help/create.png