attachy 0.1.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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/LICENSE +21 -0
  4. data/README.md +108 -0
  5. data/lib/assets/javascripts/attachy.js +289 -0
  6. data/lib/attachy.rb +13 -0
  7. data/lib/attachy/builders/attachy/form_builder.rb +15 -0
  8. data/lib/attachy/engine.rb +16 -0
  9. data/lib/attachy/helpers/attachy/view_helper.rb +31 -0
  10. data/lib/attachy/models/attachy/extension.rb +79 -0
  11. data/lib/attachy/models/attachy/file.rb +59 -0
  12. data/lib/attachy/models/attachy/viewer.rb +170 -0
  13. data/lib/attachy/version.rb +3 -0
  14. data/lib/generators/attachy/install_generator.rb +25 -0
  15. data/lib/generators/attachy/templates/config/attachy.yml.erb +15 -0
  16. data/lib/generators/attachy/templates/db/migrate/create_attachy_files_table.rb +19 -0
  17. data/lib/generators/attachy/templates/public/cloudinary_cors.html +46 -0
  18. data/spec/builders/attachy/form_builder/attachy_content_spec.rb +35 -0
  19. data/spec/builders/attachy/form_builder/attachy_file_field_spec.rb +23 -0
  20. data/spec/builders/attachy/form_builder/attachy_spec.rb +35 -0
  21. data/spec/factories/attachy/file.rb +12 -0
  22. data/spec/factories/user.rb +5 -0
  23. data/spec/helpers/attachy/attachy_content_spec.rb +21 -0
  24. data/spec/helpers/attachy/attachy_file_field_spec.rb +21 -0
  25. data/spec/helpers/attachy/attachy_spec.rb +35 -0
  26. data/spec/models/attachy/callback/destroy_file_spec.rb +55 -0
  27. data/spec/models/attachy/callback/remove_tmp_tag_spec.rb +11 -0
  28. data/spec/models/attachy/extension/user/avatar_spec.rb +91 -0
  29. data/spec/models/attachy/extension/user/photos_spec.rb +88 -0
  30. data/spec/models/attachy/file/config_spec.rb +11 -0
  31. data/spec/models/attachy/file/default_spec.rb +26 -0
  32. data/spec/models/attachy/file/path_spec.rb +16 -0
  33. data/spec/models/attachy/file/transform_spec.rb +86 -0
  34. data/spec/models/attachy/file_spec.rb +14 -0
  35. data/spec/models/attachy/viewer/attachments_spec.rb +28 -0
  36. data/spec/models/attachy/viewer/button_label_options_spec.rb +15 -0
  37. data/spec/models/attachy/viewer/button_label_spec.rb +34 -0
  38. data/spec/models/attachy/viewer/content_options_spec.rb +29 -0
  39. data/spec/models/attachy/viewer/content_spec.rb +62 -0
  40. data/spec/models/attachy/viewer/field_options_spec.rb +18 -0
  41. data/spec/models/attachy/viewer/field_spec.rb +56 -0
  42. data/spec/models/attachy/viewer/file_button_options_spec.rb +18 -0
  43. data/spec/models/attachy/viewer/file_button_spec.rb +57 -0
  44. data/spec/models/attachy/viewer/file_field_options_spec.rb +90 -0
  45. data/spec/models/attachy/viewer/file_field_spec.rb +25 -0
  46. data/spec/models/attachy/viewer/hidden_field_spec.rb +22 -0
  47. data/spec/models/attachy/viewer/image_spec.rb +170 -0
  48. data/spec/models/attachy/viewer/link_options_spec.rb +18 -0
  49. data/spec/models/attachy/viewer/link_spec.rb +131 -0
  50. data/spec/models/attachy/viewer/node_options_spec.rb +18 -0
  51. data/spec/models/attachy/viewer/node_spec.rb +134 -0
  52. data/spec/models/attachy/viewer/nodes_spec.rb +21 -0
  53. data/spec/models/attachy/viewer/remove_button_options_spec.rb +18 -0
  54. data/spec/models/attachy/viewer/transform_spec.rb +44 -0
  55. data/spec/models/attachy/viewer/value_spec.rb +83 -0
  56. data/spec/models/user_spec.rb +9 -0
  57. data/spec/rails_helper.rb +11 -0
  58. data/spec/support/common.rb +20 -0
  59. data/spec/support/database_cleaner.rb +19 -0
  60. data/spec/support/db/migrate/create_users_table.rb +7 -0
  61. data/spec/support/factory_girl.rb +7 -0
  62. data/spec/support/html_matchers.rb +5 -0
  63. data/spec/support/migrate.rb +4 -0
  64. data/spec/support/models/user.rb +5 -0
  65. data/spec/support/shoulda.rb +8 -0
  66. metadata +365 -0
@@ -0,0 +1,46 @@
1
+ <!DOCTYPE HTML>
2
+
3
+ <html>
4
+ <head>
5
+ <meta charset="utf-8">
6
+ </head>
7
+ <body>
8
+ <script>
9
+ /*
10
+ json2.js
11
+ 2016-10-28
12
+
13
+ Public Domain.
14
+
15
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
16
+
17
+ See http://www.JSON.org/js.html
18
+ This code should be minified before deployment.
19
+
20
+ See http://javascript.crockford.com/jsmin.html
21
+
22
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
23
+ NOT CONTROL.
24
+ */
25
+
26
+ "object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(a){return a<10?"0"+a:a}function this_value(){return this.valueOf()}function quote(a){return rx_escapable.lastIndex=0,rx_escapable.test(a)?'"'+a.replace(rx_escapable,function(a){var b=meta[a];return"string"==typeof b?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,h,g=gap,i=b[a];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(a)),"function"==typeof rep&&(i=rep.call(b,a,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,h=[],"[object Array]"===Object.prototype.toString.apply(i)){for(f=i.length,c=0;c<f;c+=1)h[c]=str(c,i)||"null";return e=0===h.length?"[]":gap?"[\n"+gap+h.join(",\n"+gap)+"\n"+g+"]":"["+h.join(",")+"]",gap=g,e}if(rep&&"object"==typeof rep)for(f=rep.length,c=0;c<f;c+=1)"string"==typeof rep[c]&&(d=rep[c],e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e));else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e));return e=0===h.length?"{}":gap?"{\n"+gap+h.join(",\n"+gap)+"\n"+g+"}":"{"+h.join(",")+"}",gap=g,e}}var rx_one=/^[\],:{}\s]*$/,rx_two=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,rx_three=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,rx_four=/(?:^|:|,)(?:\s*\[)+/g,rx_escapable=/[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,rx_dangerous=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},Boolean.prototype.toJSON=this_value,Number.prototype.toJSON=this_value,String.prototype.toJSON=this_value);var gap,indent,meta,rep;"function"!=typeof JSON.stringify&&(meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(a,b,c){var d;if(gap="",indent="","number"==typeof c)for(d=0;d<c;d+=1)indent+=" ";else"string"==typeof c&&(indent=c);if(rep=b,b&&"function"!=typeof b&&("object"!=typeof b||"number"!=typeof b.length))throw new Error("JSON.stringify");return str("",{"":a})}),"function"!=typeof JSON.parse&&(JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&"object"==typeof e)for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),void 0!==d?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;if(text=String(text),rx_dangerous.lastIndex=0,rx_dangerous.test(text)&&(text=text.replace(rx_dangerous,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})),rx_one.test(text.replace(rx_two,"@").replace(rx_three,"]").replace(rx_four,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}();
27
+ /* end of json2.js */
28
+
29
+ function parse(query) {
30
+ var
31
+ result = {},
32
+ params = query.split('&');
33
+
34
+ for (var i = 0; i < params.length; i++) {
35
+ var param = params[i].split('=');
36
+
37
+ result[param[0]] = decodeURIComponent(param[1]);
38
+ }
39
+
40
+ return JSON.stringify(result);
41
+ }
42
+
43
+ document.body.textContent = document.body.innerText = parse(window.location.search.slice(1));
44
+ </script>
45
+ </body>
46
+ </html>
@@ -0,0 +1,35 @@
1
+ require 'rails_helper'
2
+
3
+ class Dummy < ActionView::Helpers::FormBuilder
4
+ include Attachy::FormBuilder
5
+ end
6
+
7
+ class DummyHelper
8
+ include Attachy::ViewHelper
9
+ end
10
+
11
+ RSpec.describe Dummy, '.attachy_content' do
12
+ let!(:method) { :avatar }
13
+ let!(:options) { { key: :value } }
14
+ let!(:object) { create :user }
15
+ let!(:template) { DummyHelper.new }
16
+ let!(:dummy) { described_class.new method, object, template, options }
17
+
18
+ context 'with no block' do
19
+ xit 'delegates to view helper' do
20
+ expect(template).to receive(:attachy_content).with(method, object, options, nil)
21
+
22
+ dummy.attachy_content method, options
23
+ end
24
+ end
25
+
26
+ context 'with block' do
27
+ let!(:block) { proc {} }
28
+
29
+ xit 'delegates to view helper with block' do
30
+ expect(template).to receive(:attachy_content).with(method, object, options, block)
31
+
32
+ dummy.attachy_content method, options, &block
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ require 'rails_helper'
2
+
3
+ class Dummy < ActionView::Helpers::FormBuilder
4
+ include Attachy::FormBuilder
5
+ end
6
+
7
+ class DummyHelper
8
+ include Attachy::ViewHelper
9
+ end
10
+
11
+ RSpec.describe Dummy, '.attachy_file_field' do
12
+ let!(:method) { :avatar }
13
+ let!(:options) { { key: :value } }
14
+ let!(:object) { create :user }
15
+ let!(:template) { DummyHelper.new }
16
+ let!(:dummy) { described_class.new method, object, template, options }
17
+
18
+ it 'delegates to view helper' do
19
+ expect(template).to receive(:attachy_file_field).with(method, object, options)
20
+
21
+ dummy.attachy_file_field method, options
22
+ end
23
+ end
@@ -0,0 +1,35 @@
1
+ require 'rails_helper'
2
+
3
+ class Dummy < ActionView::Helpers::FormBuilder
4
+ include Attachy::FormBuilder
5
+ end
6
+
7
+ class DummyHelper
8
+ include Attachy::ViewHelper
9
+ end
10
+
11
+ RSpec.describe Dummy, '.attachy' do
12
+ let!(:method) { :avatar }
13
+ let!(:options) { { key: :value } }
14
+ let!(:object) { create :user }
15
+ let!(:template) { DummyHelper.new }
16
+ let!(:dummy) { described_class.new method, object, template, options }
17
+
18
+ context 'with no block' do
19
+ it 'delegates to view helper' do
20
+ expect(template).to receive(:attachy).with(method, object, options, nil)
21
+
22
+ dummy.attachy method, options
23
+ end
24
+ end
25
+
26
+ context 'with block' do
27
+ let!(:block) { proc {} }
28
+
29
+ it 'delegates to view helper with block' do
30
+ expect(template).to receive(:attachy).with(method, object, options, block)
31
+
32
+ dummy.attachy method, options, &block
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,12 @@
1
+ FactoryGirl.define do
2
+ factory :file, class: Attachy::File do
3
+ format :jpg
4
+ height 600
5
+ scope :avatar
6
+ width 800
7
+
8
+ sequence(:public_id) { |i| "PublicId#{i}" }
9
+
10
+ sequence :version, &:to_s
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ FactoryGirl.define do
2
+ factory :user do
3
+ sequence(:name) { |i| "Name #{i}" }
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'rails_helper'
2
+
3
+ class DummyHelper
4
+ include Attachy::ViewHelper
5
+ end
6
+
7
+ RSpec.describe DummyHelper, '.attachy_content' do
8
+ let!(:method) { :avatar }
9
+ let!(:options) { { key: :value } }
10
+ let!(:object) { create :user }
11
+ let!(:helper) { DummyHelper.new }
12
+ let!(:viewer) { double Attachy::Viewer, content: :content }
13
+
14
+ before do
15
+ allow(Attachy::Viewer).to receive(:new).with(method, object, options, helper) { viewer }
16
+ end
17
+
18
+ it 'calls content from viewer' do
19
+ expect(helper.attachy_content(method, object, options)).to eq :content
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'rails_helper'
2
+
3
+ class DummyHelper
4
+ include Attachy::ViewHelper
5
+ end
6
+
7
+ RSpec.describe DummyHelper, '.attachy_file_field' do
8
+ let!(:method) { :avatar }
9
+ let!(:options) { { key: :value } }
10
+ let!(:object) { create :user }
11
+ let!(:helper) { DummyHelper.new }
12
+ let!(:viewer) { double Attachy::Viewer, file_field: :file_field }
13
+
14
+ before do
15
+ allow(Attachy::Viewer).to receive(:new).with(method, object, options, helper) { viewer }
16
+ end
17
+
18
+ it 'calls file_field from viewer' do
19
+ expect(helper.attachy_file_field(method, object, options)).to eq :file_field
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ require 'rails_helper'
2
+
3
+ class DummyHelper
4
+ include Attachy::ViewHelper
5
+ end
6
+
7
+ RSpec.describe DummyHelper, '.attachy' do
8
+ let!(:method) { :avatar }
9
+ let!(:options) { { key: :value } }
10
+ let!(:object) { create :user }
11
+ let!(:helper) { DummyHelper.new }
12
+ let!(:viewer) { double Attachy::Viewer, field: :field }
13
+
14
+ context 'with no block' do
15
+ before do
16
+ allow(Attachy::Viewer).to receive(:new).with(method, object, options, helper) { viewer }
17
+ end
18
+
19
+ it 'calls field from viewer' do
20
+ expect(helper.attachy(method, object, options, nil)).to eq :field
21
+ end
22
+ end
23
+
24
+ context 'with block' do
25
+ let!(:block) { proc { |v| expect(v.field).to eq :field } }
26
+
27
+ before do
28
+ allow(Attachy::Viewer).to receive(:new).with(method, object, options, helper) { viewer }
29
+ end
30
+
31
+ it 'delegates to view helper with block' do
32
+ helper.attachy method, object, options, block
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,55 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe User, ':destroy_file' do
4
+ let!(:user) { create :user }
5
+
6
+ let!(:photo) do
7
+ expect(Cloudinary::Uploader).to receive(:remove_tag)
8
+
9
+ create :file, public_id: 'public_id', scope: :photos, attachable: user
10
+ end
11
+
12
+ context 'via assign' do
13
+ context 'via assign on relation' do
14
+ it 'removes the file via api' do
15
+ expect(Cloudinary::Uploader).to receive(:destroy).with('public_id')
16
+
17
+ user.photos_files = []
18
+ end
19
+ end
20
+
21
+ context 'via assign on scope' do
22
+ it 'removes the file via api' do
23
+ expect(Cloudinary::Uploader).to receive(:destroy).with('public_id')
24
+
25
+ user.photos = '[]'
26
+ end
27
+ end
28
+ end
29
+
30
+ context 'via method' do
31
+ context 'via method over one record' do
32
+ it 'removes the file via api' do
33
+ expect(Cloudinary::Uploader).to receive(:destroy).with('public_id')
34
+
35
+ user.photos_files.first.destroy
36
+ end
37
+ end
38
+
39
+ context 'via method over criteria' do
40
+ it 'removes the file via api' do
41
+ expect(Cloudinary::Uploader).to receive(:destroy).with('public_id')
42
+
43
+ user.photos_files.destroy_all
44
+ end
45
+ end
46
+
47
+ context 'via .clear' do
48
+ it 'does not removes the file via api' do
49
+ expect(Cloudinary::Uploader).not_to receive(:destroy)
50
+
51
+ user.photos_files.clear
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe User, ':remove_tmp_tag' do
4
+ let!(:user) { create :user }
5
+
6
+ it 'removes the tmp tag via api' do
7
+ expect(Cloudinary::Uploader).to receive(:remove_tag).with(Attachy::TMP_TAG, ['public_id'])
8
+
9
+ create :file, public_id: 'public_id', attachable: user
10
+ end
11
+ end
@@ -0,0 +1,91 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe User, ':avatar' do
4
+ before do
5
+ allow(Cloudinary::Uploader).to receive(:remove_tag)
6
+ allow(Cloudinary::Uploader).to receive(:destroy)
7
+ end
8
+
9
+ describe ':avatar_files' do
10
+ let!(:user) { create :user }
11
+ let!(:avatar_1) { create :file, scope: :avatar, attachable: user }
12
+ let!(:avatar_2) { create :file, scope: :avatar, attachable: user }
13
+
14
+ it 'returns all records even in the singular' do
15
+ expect(user.avatar_files).to match_array [avatar_1, avatar_2]
16
+ end
17
+ end
18
+
19
+ describe ':avatar_files=' do
20
+ let!(:user) { create :user }
21
+ let!(:avatar_1) { create :file, scope: :avatar }
22
+ let!(:avatar_2) { create :file, scope: :avatar }
23
+
24
+ context 'when given records' do
25
+ before { user.avatar_files = [avatar_1, avatar_2] }
26
+
27
+ it 'is saved' do
28
+ expect(user.avatar_files).to match_array [avatar_1, avatar_2]
29
+ end
30
+ end
31
+
32
+ context 'when given no records' do
33
+ before { user.avatar_files = [avatar_1, avatar_2] }
34
+
35
+ it 'clears the existents' do
36
+ user.avatar_files = []
37
+
38
+ expect(user.avatar_files).to eq []
39
+ end
40
+ end
41
+ end
42
+
43
+ describe ':avatar' do
44
+ let!(:user) { create :user }
45
+
46
+ context 'with no file' do
47
+ before do
48
+ allow(Attachy::File).to receive(:default) { :default }
49
+ end
50
+
51
+ it 'returns a default file' do
52
+ expect(user.avatar).to eq :default
53
+ end
54
+ end
55
+
56
+ context 'with file' do
57
+ let!(:avatar_1) { create :file, scope: :avatar, attachable: user }
58
+ let!(:avatar_2) { create :file, scope: :avatar, attachable: user }
59
+
60
+ it 'returns just the last one simulating a has_one' do
61
+ expect(user.avatar).to eq avatar_2
62
+ end
63
+ end
64
+ end
65
+
66
+ describe ':avatar?' do
67
+ let!(:user) { create :user }
68
+
69
+ context 'with no records' do
70
+ specify { expect(user.avatar?).to eq false }
71
+ end
72
+
73
+ context 'with records' do
74
+ before { create :file, scope: :avatar, attachable: user }
75
+
76
+ specify { expect(user.avatar?).to eq true }
77
+ end
78
+ end
79
+
80
+ describe ':avatar_metadata' do
81
+ let!(:user) { create :user }
82
+
83
+ it 'returns the metadata' do
84
+ expect(user.avatar_metadata).to eq(
85
+ accept: %i[jpg png],
86
+ multiple: false,
87
+ scope: :avatar
88
+ )
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,88 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe User, ':photos' do
4
+ before do
5
+ allow(Cloudinary::Uploader).to receive(:remove_tag)
6
+ allow(Cloudinary::Uploader).to receive(:destroy)
7
+ end
8
+
9
+ describe ':photos_files' do
10
+ let!(:user) { create :user }
11
+ let!(:photo_1) { create :file, scope: :photos, attachable: user }
12
+ let!(:photo_2) { create :file, scope: :photos, attachable: user }
13
+
14
+ it 'returns all records' do
15
+ expect(user.photos_files).to match_array [photo_1, photo_2]
16
+ end
17
+ end
18
+
19
+ describe ':photos_files=' do
20
+ let!(:user) { create :user }
21
+ let!(:photo_1) { create :file, scope: :photo }
22
+ let!(:photo_2) { create :file, scope: :photo }
23
+
24
+ context 'when given records' do
25
+ before { user.photos_files = [photo_1, photo_2] }
26
+
27
+ it 'is saved' do
28
+ expect(user.photos_files).to match_array [photo_1, photo_2]
29
+ end
30
+ end
31
+
32
+ context 'when given no records' do
33
+ before { user.photos_files = [photo_1, photo_2] }
34
+
35
+ it 'clears the existents' do
36
+ user.photos_files = []
37
+
38
+ expect(user.photos_files).to eq []
39
+ end
40
+ end
41
+ end
42
+
43
+ describe ':photo' do
44
+ let!(:user) { create :user }
45
+
46
+ context 'with no file' do
47
+ it 'returns empty' do
48
+ expect(user.photos).to eq []
49
+ end
50
+ end
51
+
52
+ context 'with file' do
53
+ let!(:photo_1) { create :file, scope: :photos, attachable: user }
54
+ let!(:photo_2) { create :file, scope: :photos, attachable: user }
55
+
56
+ it 'returns all records' do
57
+ expect(user.photos).to match_array [photo_1, photo_2]
58
+ end
59
+ end
60
+ end
61
+
62
+ describe ':photo?' do
63
+ let!(:user) { create :user }
64
+
65
+ context 'with no records' do
66
+ specify { expect(user.photos?).to eq false }
67
+ end
68
+
69
+ context 'with records' do
70
+ before { create :file, scope: :photos, attachable: user }
71
+
72
+ specify { expect(user.photos?).to eq true }
73
+ end
74
+ end
75
+
76
+ describe ':photos_metadata' do
77
+ let!(:user) { create :user }
78
+
79
+ it 'returns the metadata' do
80
+ expect(user.photos_metadata).to eq(
81
+ accept: %i[jpg png],
82
+ maximum: 10,
83
+ multiple: true,
84
+ scope: :photos
85
+ )
86
+ end
87
+ end
88
+ end