populate-me 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +20 -0
  5. data/README.md +655 -0
  6. data/Rakefile +14 -0
  7. data/example/config.ru +100 -0
  8. data/lib/populate_me.rb +2 -0
  9. data/lib/populate_me/admin.rb +157 -0
  10. data/lib/populate_me/admin/__assets__/css/asmselect.css +63 -0
  11. data/lib/populate_me/admin/__assets__/css/jquery-ui.min.css +6 -0
  12. data/lib/populate_me/admin/__assets__/css/main.css +244 -0
  13. data/lib/populate_me/admin/__assets__/img/help/children.png +0 -0
  14. data/lib/populate_me/admin/__assets__/img/help/create.png +0 -0
  15. data/lib/populate_me/admin/__assets__/img/help/delete.png +0 -0
  16. data/lib/populate_me/admin/__assets__/img/help/edit.png +0 -0
  17. data/lib/populate_me/admin/__assets__/img/help/form.png +0 -0
  18. data/lib/populate_me/admin/__assets__/img/help/list.png +0 -0
  19. data/lib/populate_me/admin/__assets__/img/help/login.png +0 -0
  20. data/lib/populate_me/admin/__assets__/img/help/logout.png +0 -0
  21. data/lib/populate_me/admin/__assets__/img/help/menu.png +0 -0
  22. data/lib/populate_me/admin/__assets__/img/help/overview.png +0 -0
  23. data/lib/populate_me/admin/__assets__/img/help/save.png +0 -0
  24. data/lib/populate_me/admin/__assets__/img/help/sort.png +0 -0
  25. data/lib/populate_me/admin/__assets__/img/help/sublist.png +0 -0
  26. data/lib/populate_me/admin/__assets__/js/asmselect.js +412 -0
  27. data/lib/populate_me/admin/__assets__/js/columnav.js +87 -0
  28. data/lib/populate_me/admin/__assets__/js/jquery-ui.min.js +7 -0
  29. data/lib/populate_me/admin/__assets__/js/main.js +388 -0
  30. data/lib/populate_me/admin/__assets__/js/mustache.js +578 -0
  31. data/lib/populate_me/admin/__assets__/js/sortable.js +2 -0
  32. data/lib/populate_me/admin/views/help.erb +94 -0
  33. data/lib/populate_me/admin/views/page.erb +189 -0
  34. data/lib/populate_me/api.rb +124 -0
  35. data/lib/populate_me/attachment.rb +186 -0
  36. data/lib/populate_me/document.rb +192 -0
  37. data/lib/populate_me/document_mixins/admin_adapter.rb +149 -0
  38. data/lib/populate_me/document_mixins/callbacks.rb +125 -0
  39. data/lib/populate_me/document_mixins/outcasting.rb +83 -0
  40. data/lib/populate_me/document_mixins/persistence.rb +95 -0
  41. data/lib/populate_me/document_mixins/schema.rb +198 -0
  42. data/lib/populate_me/document_mixins/typecasting.rb +70 -0
  43. data/lib/populate_me/document_mixins/validation.rb +44 -0
  44. data/lib/populate_me/file_system_attachment.rb +40 -0
  45. data/lib/populate_me/grid_fs_attachment.rb +103 -0
  46. data/lib/populate_me/mongo.rb +160 -0
  47. data/lib/populate_me/s3_attachment.rb +120 -0
  48. data/lib/populate_me/variation.rb +38 -0
  49. data/lib/populate_me/version.rb +4 -0
  50. data/populate-me.gemspec +34 -0
  51. data/test/helper.rb +37 -0
  52. data/test/test_admin.rb +183 -0
  53. data/test/test_api.rb +246 -0
  54. data/test/test_attachment.rb +167 -0
  55. data/test/test_document.rb +128 -0
  56. data/test/test_document_admin_adapter.rb +221 -0
  57. data/test/test_document_callbacks.rb +151 -0
  58. data/test/test_document_outcasting.rb +247 -0
  59. data/test/test_document_persistence.rb +83 -0
  60. data/test/test_document_schema.rb +280 -0
  61. data/test/test_document_typecasting.rb +128 -0
  62. data/test/test_grid_fs_attachment.rb +239 -0
  63. data/test/test_mongo.rb +324 -0
  64. data/test/test_s3_attachment.rb +281 -0
  65. data/test/test_variation.rb +91 -0
  66. data/test/test_version.rb +11 -0
  67. metadata +294 -0
@@ -0,0 +1,280 @@
1
+ require 'helper'
2
+ require 'populate_me/document'
3
+
4
+ describe PopulateMe::Document, 'Schema' do
5
+
6
+ parallelize_me!
7
+
8
+ describe "Relationships" do
9
+
10
+ class Relative < PopulateMe::Document
11
+ relationship :siblings
12
+ relationship :home_remedies
13
+ relationship :friends, label: 'Budies', class_name: 'Budy', foreign_key: :budy_id, dependent: false
14
+ end
15
+
16
+ class Relative::Sibling < PopulateMe::Document
17
+ field :size
18
+ field :relative_id, type: :parent
19
+ end
20
+
21
+ before do
22
+ Relative.documents = []
23
+ Relative::Sibling.documents = []
24
+ end
25
+
26
+ it "Defaults class name" do
27
+ assert_equal "Relative::Sibling", Relative.relationships[:siblings][:class_name]
28
+ assert_equal "Relative::HomeRemedy", Relative.relationships[:home_remedies][:class_name]
29
+ end
30
+
31
+ it "Defaults label" do
32
+ assert_equal "Siblings", Relative.relationships[:siblings][:label]
33
+ assert_equal "Home remedies", Relative.relationships[:home_remedies][:label]
34
+ end
35
+
36
+ it "Defaults foreign key" do
37
+ assert_equal :relative_id, Relative.relationships[:siblings][:foreign_key]
38
+ assert_equal :relative_id, Relative.relationships[:home_remedies][:foreign_key]
39
+ end
40
+
41
+ it "Defaults :dependent to true" do
42
+ assert Relative.relationships[:siblings][:dependent]
43
+ end
44
+
45
+ it "Has everything editable" do
46
+ assert_equal "Budies", Relative.relationships[:friends][:label]
47
+ assert_equal "Budy", Relative.relationships[:friends][:class_name]
48
+ assert_equal :budy_id, Relative.relationships[:friends][:foreign_key]
49
+ refute Relative.relationships[:friends][:dependent]
50
+ end
51
+
52
+ it "Creates a getter for cached items" do
53
+ relative = Relative.new(id: 10)
54
+ relative.save
55
+ Relative::Sibling.new(relative_id: 10, size: 'S').save
56
+ Relative::Sibling.new(relative_id: 10, size: 'M').save
57
+ Relative::Sibling.new(relative_id: 10, size: 'L').save
58
+ assert relative.respond_to? :siblings
59
+ assert_nil relative.instance_variable_get('@cached_siblings')
60
+ siblings = relative.siblings
61
+ assert_equal 3, siblings.size
62
+ assert_equal siblings, relative.instance_variable_get('@cached_siblings')
63
+ end
64
+
65
+ it "Creates a getter for cached first item" do
66
+ relative = Relative.new(id: 10)
67
+ relative.save
68
+ Relative::Sibling.new(relative_id: 10, size: 'S').save
69
+ Relative::Sibling.new(relative_id: 10, size: 'M').save
70
+ Relative::Sibling.new(relative_id: 10, size: 'L').save
71
+ assert relative.respond_to? :siblings_first
72
+ assert_nil relative.instance_variable_get('@cached_siblings_first')
73
+ sibling = relative.siblings_first
74
+ assert_equal 'S', sibling.size
75
+ assert_equal sibling, relative.instance_variable_get('@cached_siblings_first')
76
+ end
77
+
78
+ end
79
+
80
+ describe '::to_select_options' do
81
+
82
+ class Selectoptionable < PopulateMe::Document
83
+ field :name
84
+ field :slug
85
+ label :slug
86
+ end
87
+
88
+ class Selectpreviewable < PopulateMe::Document
89
+ set :default_attachment_class, PopulateMe::Attachment
90
+ field :name
91
+ field :img, type: :attachment, variations: [
92
+ PopulateMe::Variation.default
93
+ ]
94
+ end
95
+
96
+ before do
97
+ Selectoptionable.documents = []
98
+ Selectoptionable.new(id: '1', name: 'Joe', slug: 'joe').save
99
+ Selectoptionable.new(id: '2', name: 'William', slug: 'william').save
100
+ Selectoptionable.new(id: '3', name: 'Jack', slug: 'jack').save
101
+ Selectoptionable.new(id: '4', name: 'Averell', slug: 'averell').save
102
+ Selectpreviewable.documents = []
103
+ Selectpreviewable.new(id: '1', name: 'Project', img: 'project.jpg').save
104
+ end
105
+
106
+ after do
107
+ Selectoptionable.documents = []
108
+ end
109
+
110
+ it 'Formats all items for a select_options' do
111
+ output_proc = Selectoptionable.to_select_options
112
+ assert output_proc.is_a?(Proc)
113
+ output = output_proc.call
114
+ assert_equal 4, output.size
115
+ assert output.all?{|o| o.is_a?(Hash) and o.size==2}
116
+ assert_equal '1', output.find{|o|o.fetch(:description)=='joe'}.fetch(:value)
117
+ end
118
+
119
+ it 'Puts items in alphabetical order of their label' do
120
+ output= Selectoptionable.to_select_options.call
121
+ assert_equal({description: 'averell', value: '4'}, output[0])
122
+ end
123
+
124
+ it 'Has an option for prepending empty choice' do
125
+ output= Selectoptionable.to_select_options(allow_empty: true).call
126
+ assert_equal({description: '?', value: ''}, output[0])
127
+ assert_equal({description: 'averell', value: '4'}, output[1])
128
+ end
129
+
130
+ it 'Adds a :preview_uri when there is a thumbnail' do
131
+ output= Selectpreviewable.to_select_options.call
132
+ assert output[0].key?(:preview_uri)
133
+ assert_equal 'Project', output[0][:description]
134
+ assert_equal '1', output[0][:value]
135
+ assert_equal '/attachment/selectpreviewable/project.populate_me_thumb.jpg', output[0][:preview_uri]
136
+ end
137
+
138
+ end
139
+
140
+ describe 'Polymorphism' do
141
+
142
+ class PolyBox < PopulateMe::Document
143
+ field :first_name
144
+ field :last_name
145
+ field :middle_name, only_for: ['Middle', 'Long name']
146
+ field :nick_name, only_for: 'Funny'
147
+ field :third_name, only_for: 'Long name'
148
+ end
149
+
150
+ class NotPoly < PopulateMe::Document
151
+ field :name
152
+ relationship :images
153
+ end
154
+
155
+ class JustPoly < PopulateMe::Document
156
+ polymorphic
157
+ end
158
+
159
+ class PolyCustom < PopulateMe::Document
160
+ polymorphic type: :text, custom_option: 'Custom'
161
+ end
162
+
163
+ class PolyApplicable < PopulateMe::Document
164
+ field :name_1, only_for: 'Shape 1'
165
+ field :name_2, only_for: 'Shape 2'
166
+ field :position
167
+ end
168
+
169
+ class PolyLabel < PopulateMe::Document
170
+ polymorphic
171
+ field :name
172
+ end
173
+
174
+ class PolyRelationship < PopulateMe::Document
175
+ field :name
176
+ field :short_description, only_for: 'Chapter'
177
+ relationship :images, only_for: 'Slider'
178
+ relationship :paragraphs, only_for: 'Chapter'
179
+ end
180
+
181
+ class PolyGroup < PopulateMe::Document
182
+ only_for 'Slider' do
183
+ field :name
184
+ relationship :images
185
+ end
186
+ field :no_only_for
187
+ only_for ['Try','This'] do
188
+ field :crazy
189
+ end
190
+ field :position
191
+ end
192
+
193
+ it 'Creates a field for polymorphic type if it does not exist yet' do
194
+ assert_equal :polymorphic_type, PolyBox.fields[:polymorphic_type][:type]
195
+ assert_equal :polymorphic_type, JustPoly.fields[:polymorphic_type][:type]
196
+ assert_equal :polymorphic_type, PolyRelationship.fields[:polymorphic_type][:type]
197
+ end
198
+
199
+ it 'By default polymorphic_type is hidden and not wrapped' do
200
+ assert_equal :hidden, PolyBox.fields[:polymorphic_type][:input_attributes][:type]
201
+ refute PolyBox.fields[:polymorphic_type][:wrap]
202
+ end
203
+
204
+ it 'Does not create polymorphic type field if not required' do
205
+ assert_nil NotPoly.fields[:polymorphic_type]
206
+ end
207
+
208
+ it 'Gathers all polymorphic type unique values' do
209
+ assert_equal ['Middle', 'Long name', 'Funny'], PolyBox.fields[:polymorphic_type][:values]
210
+ assert_equal [], JustPoly.fields[:polymorphic_type][:values]
211
+ assert_equal ['Chapter', 'Slider'], PolyRelationship.fields[:polymorphic_type][:values]
212
+ end
213
+
214
+ it 'Adds or update field options for polymorphic type passed as arguments' do
215
+ assert_equal :text, PolyCustom.fields[:polymorphic_type][:type]
216
+ assert_equal 'Custom', PolyCustom.fields[:polymorphic_type][:custom_option]
217
+ end
218
+
219
+ it 'Forces only_for field option to be an Array if String' do
220
+ assert_nil PolyBox.fields[:first_name][:only_for]
221
+ assert_equal ['Middle', 'Long name'], PolyBox.fields[:middle_name][:only_for]
222
+ assert_equal ['Funny'], PolyBox.fields[:nick_name][:only_for]
223
+ assert_equal ['Slider'], PolyRelationship.relationships[:images][:only_for]
224
+ end
225
+
226
+ it 'Has a polymorphic? predicate' do
227
+ assert PolyBox.polymorphic?
228
+ refute NotPoly.polymorphic?
229
+ end
230
+
231
+ it 'Knows when a field is applicable to a polymorphic_type' do
232
+ assert NotPoly.field_applicable?(:name, nil) # Not polymorphic
233
+ assert NotPoly.field_applicable?(:name, 'Fake') # Not polymorphic
234
+ refute NotPoly.field_applicable?(:non_existing_field, nil) # Not polymorphic
235
+ assert PolyApplicable.field_applicable?(:polymorphic_type, 'Shape 1') # no only_for
236
+ assert PolyApplicable.field_applicable?(:position, 'Shape 1') # no only_for
237
+ refute PolyApplicable.field_applicable?(:name_2, 'Shape 1') # not included
238
+ assert PolyApplicable.field_applicable?(:name_2, nil) # no type set yet
239
+
240
+ assert NotPoly.new.field_applicable?(:name) # Not polymorphic
241
+ refute NotPoly.new.field_applicable?(:non_existing_field) # Not polymorphic
242
+ assert PolyApplicable.new(polymorphic_type: 'Shape 2').field_applicable?(:name_2)
243
+ refute PolyApplicable.new(polymorphic_type: 'Shape 2').field_applicable?(:name_1)
244
+ assert PolyApplicable.new.field_applicable?(:name_2) # No type set yet
245
+ assert PolyApplicable.new.field_applicable?(:name_1) # No type set yes
246
+ end
247
+
248
+ it 'Knows when a relationship is applicable to a polymorphic_type' do
249
+ assert PolyRelationship.relationship_applicable?(:images, 'Slider')
250
+ refute PolyRelationship.relationship_applicable?(:images, 'Chapter')
251
+ assert PolyRelationship.new(polymorphic_type: 'Slider').relationship_applicable?(:images)
252
+ refute PolyRelationship.new(polymorphic_type: 'Chapter').relationship_applicable?(:images)
253
+ end
254
+
255
+ it 'Ignores polymorphic_type when picking the default label_field' do
256
+ assert_equal :name, PolyLabel.label_field
257
+ end
258
+
259
+ it 'Uses groups to define only_for option for all included fields and relationships' do
260
+ assert_equal :polymorphic_type, PolyGroup.fields[:polymorphic_type][:type]
261
+ assert_equal ['Slider', 'Try', 'This'], PolyGroup.fields[:polymorphic_type][:values]
262
+ assert_equal ['Slider'], PolyGroup.fields[:name][:only_for]
263
+ assert_equal ['Slider'], PolyGroup.relationships[:images][:only_for]
264
+ assert_equal ['Try','This'], PolyGroup.fields[:crazy][:only_for]
265
+ end
266
+
267
+ it 'Group only_for option does not leak on special fields' do
268
+ refute PolyGroup.fields[:id].key?(:only_for)
269
+ refute PolyGroup.fields[:polymorphic_type].key?(:only_for)
270
+ end
271
+
272
+ it 'Group only_for option does not leak outside of their scope' do
273
+ refute PolyGroup.fields[:no_only_for].key?(:only_for)
274
+ refute PolyGroup.fields[:position].key?(:only_for)
275
+ end
276
+
277
+ end
278
+
279
+ end
280
+
@@ -0,0 +1,128 @@
1
+ require 'helper'
2
+ require 'populate_me/document'
3
+
4
+ class Person < PopulateMe::Document
5
+ field :name
6
+ field :shared, type: :boolean
7
+ field :age, type: :integer
8
+ field :salary, type: :price
9
+ field :size, type: :select, select_option: ['S','M','L']
10
+ field :categories, type: :select, select_option: ['A','B','C'], multiple: true
11
+ field :dob, type: :date
12
+ field :when, type: :datetime
13
+ end
14
+
15
+ describe PopulateMe::Document, 'Typecasting' do
16
+
17
+ parallelize_me!
18
+
19
+ let(:subject_class) { Person }
20
+ subject { subject_class.new }
21
+
22
+ describe "Value is blank or nil" do
23
+ it "Returns nil" do
24
+ assert_nil subject.typecast(:name,nil)
25
+ assert_nil subject.typecast(:name,'')
26
+ assert_nil subject.typecast(:salary,'')
27
+ assert_nil subject.typecast(:dob,'')
28
+ assert_nil subject.typecast(:when,'')
29
+ end
30
+ end
31
+
32
+ describe "Field has type :string" do
33
+ it "Returns it as-is" do
34
+ assert_equal 'Bob', subject.typecast(:name,'Bob')
35
+ assert_equal 'true', subject.typecast(:name,'true')
36
+ assert_equal '5', subject.typecast(:name,'5')
37
+ end
38
+ end
39
+
40
+ describe "Field has type :boolean" do
41
+ it "Casts as a boolean" do
42
+ assert_equal true, subject.typecast(:shared,'true')
43
+ assert_equal false, subject.typecast(:shared,'false')
44
+ end
45
+ end
46
+
47
+ describe "Field has type :integer" do
48
+ describe "Value is just an integer" do
49
+ it "Casts it as integer" do
50
+ assert_equal 42, subject.typecast(:age,'42')
51
+ end
52
+ end
53
+ describe "Value has something written at the end" do
54
+ it "Ignores it" do
55
+ assert_equal 42, subject.typecast(:age,'42 yo')
56
+ end
57
+ end
58
+ describe "Value is a float" do
59
+ it "Rounds it" do
60
+ assert_equal 42, subject.typecast(:age,'42.50')
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "Field has type :price" do
66
+ describe "Value is an integer" do
67
+ it "Casts it in cents/pence (x100)" do
68
+ assert_equal 4200, subject.typecast(:salary,'42')
69
+ end
70
+ end
71
+ describe "Value is a float with 2 decimals" do
72
+ it "Casts it in cents/pence" do
73
+ assert_equal 4250, subject.typecast(:salary,'42.50')
74
+ end
75
+ end
76
+ describe "Value is a float with irregular decimals for a price" do
77
+ it "Assumes 2 digits after comma" do
78
+ assert_equal 4250, subject.typecast(:salary,'42.5')
79
+ end
80
+ end
81
+ describe "Value is prefixed or suffixed" do
82
+ it "Ignores what is not part of the price" do
83
+ assert_equal 4250, subject.typecast(:salary,'$42.5')
84
+ assert_equal 4250, subject.typecast(:salary,'42.5 Dollars')
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "Field has type :select" do
90
+ it "Removes 'nil' string if multiple/array" do
91
+ assert_equal 'M', subject.typecast(:size, 'M')
92
+ assert_equal ['A','B'], subject.typecast(:categories, ['A','B'])
93
+ assert_equal ['A','B'], subject.typecast(:categories, ['nil','A','B'])
94
+ end
95
+ end
96
+
97
+ describe "Field has type :date" do
98
+ it "Parses the date with Date.parse" do
99
+ assert_equal Date.parse('10/11/1979'), subject.typecast(:dob,'10/11/1979')
100
+ end
101
+ it "Works with yyyy-mm-dd date html input format" do
102
+ assert_equal Date.parse('10/11/1979'), subject.typecast(:dob,'1979-11-10')
103
+ end
104
+ describe "Value is malformed" do
105
+ it "Returns nil" do
106
+ assert_nil subject.typecast(:dob,'10/11')
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "Field has type :datetime" do
112
+ it "Splits the numbers and feed them to Time.utc" do
113
+ assert_equal Time.utc(1979,11,10,12,30,4), subject.typecast(:when,'10/11/1979 12:30:4')
114
+ end
115
+ describe "Delimiter is a dash" do
116
+ it "Replaces them with forward slash before parsing" do
117
+ assert_equal Time.utc(1979,11,10,12,30,4), subject.typecast(:when,'10-11-1979 12:30:4')
118
+ end
119
+ end
120
+ describe "Value is malformed" do
121
+ it "Returns nil" do
122
+ assert_nil subject.typecast(:when,'10/11')
123
+ assert_nil subject.typecast(:when,'10/11/1979')
124
+ end
125
+ end
126
+ end
127
+
128
+ end
@@ -0,0 +1,239 @@
1
+ require 'helper'
2
+ require 'populate_me/mongo'
3
+ require 'populate_me/grid_fs_attachment'
4
+
5
+ Mongo::Logger.logger.level = Logger::ERROR
6
+
7
+ GRIDMONGO = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'populate-me-grid-test')
8
+ GRIDDB = GRIDMONGO.database
9
+ GRIDDB.drop
10
+ PopulateMe::Mongo.set :db, GRIDDB
11
+ PopulateMe::Mongo.set :default_attachment_class, PopulateMe::GridFS
12
+ PopulateMe::GridFS.set :db, GRIDDB
13
+
14
+ describe 'PopulateMe::GridFS' do
15
+ # parallelize_me!
16
+
17
+ class GridBook < PopulateMe::Mongo
18
+ field :cover, type: :attachment, variations: [
19
+ PopulateMe::Variation.new_image_magick_job(:thumb, :gif, "-resize '300x'")
20
+ ]
21
+ field :content, type: :attachment, variations: [
22
+ PopulateMe::Variation.new(:upcase, :txt, lambda{ |src,dst|
23
+ Kernel.system "cat \"#{src}\" | tr 'a-z' 'A-Z' > \"#{dst}\""
24
+ })
25
+ ]
26
+ end
27
+
28
+ before do
29
+ GRIDDB.drop
30
+ end
31
+
32
+ # Utils
33
+
34
+ it 'Returns URL with url_prefix' do
35
+ book = GridBook.new cover: "candy.jpg"
36
+ assert_equal '/attachment/candy.jpg', book.attachment(:cover).url
37
+ assert_equal '/attachment/candy.thumb.gif', book.attachment(:cover).url(:thumb)
38
+ end
39
+
40
+ it 'Has nil URL when field is blank' do
41
+ book = GridBook.new
42
+ assert_nil book.attachment(:cover).url
43
+ end
44
+
45
+ it 'Has location root without attachee prefix' do
46
+ book = GridBook.new
47
+ refute_match book.attachment(:cover).attachee_prefix, book.attachment(:cover).location_root
48
+ end
49
+
50
+ # Create
51
+
52
+ it "Saves attachments on create with variations" do
53
+ book = GridBook.new
54
+
55
+ file = Tempfile.new('foo')
56
+ file.write('hello')
57
+ file.rewind
58
+
59
+ assert_equal 0, GRIDDB['fs.files'].count
60
+ field_value = book.attachment(:content).create({
61
+ tempfile: file,
62
+ filename: 'story.txt',
63
+ type: 'text/plain'
64
+ })
65
+ assert_equal 2, GRIDDB['fs.files'].count
66
+ assert_equal 'grid-book/story.txt', field_value
67
+
68
+ gridfile = GRIDDB.fs.find(filename: 'grid-book/story.txt').first
69
+ assert_equal 'text/plain', gridfile['contentType']
70
+ assert_equal 'grid-book', gridfile['metadata']['parent_collection']
71
+ GRIDDB.fs.open_download_stream(gridfile['_id']) do |stream|
72
+ assert_equal 'hello', stream.read
73
+ end
74
+
75
+ vargridfile = GRIDDB.fs.find(filename: 'grid-book/story.upcase.txt').first
76
+ assert_equal 'text/plain', vargridfile['contentType']
77
+ assert_equal 'grid-book', vargridfile['metadata']['parent_collection']
78
+ GRIDDB.fs.open_download_stream(vargridfile['_id']) do |stream|
79
+ assert_equal 'HELLO', stream.read
80
+ end
81
+
82
+ file.close
83
+ file.unlink
84
+ end
85
+
86
+ it 'Does not create 2 files with the same name' do
87
+ file = Tempfile.new('foo')
88
+
89
+ book = GridBook.new
90
+
91
+ field_value = book.attachment(:content).create({
92
+ tempfile: file,
93
+ filename: 'story.txt',
94
+ type: 'text/plain'
95
+ })
96
+
97
+ assert_equal 'grid-book/story.txt', field_value
98
+
99
+ field_value = book.attachment(:content).create({
100
+ tempfile: file,
101
+ filename: 'story.txt',
102
+ type: 'text/plain'
103
+ })
104
+
105
+ assert_equal 'grid-book/story-1.txt', field_value
106
+
107
+ field_value = book.attachment(:content).create({
108
+ tempfile: file,
109
+ filename: 'story.txt',
110
+ type: 'text/plain'
111
+ })
112
+
113
+ assert_equal 'grid-book/story-2.txt', field_value
114
+
115
+ file.close
116
+ file.unlink
117
+ end
118
+
119
+ # Delete
120
+
121
+ it 'Is deletable when field is not blank' do
122
+ book = GridBook.new cover: "candy.jpg"
123
+ assert book.attachment(:cover).deletable?
124
+ end
125
+
126
+ it 'Is not deletable when field is blank' do
127
+ book = GridBook.new
128
+ refute book.attachment(:cover).deletable?
129
+ end
130
+
131
+ it 'Deletes all attachments' do
132
+ file = Tempfile.new('foo')
133
+
134
+ book = GridBook.new
135
+
136
+ field_value = book.attachment(:content).create({
137
+ tempfile: file,
138
+ filename: 'story.txt',
139
+ type: 'text/plain'
140
+ })
141
+ book.content = field_value
142
+
143
+ refute_nil GRIDDB.fs.find(filename: 'grid-book/story.txt').first
144
+ refute_nil GRIDDB.fs.find(filename: 'grid-book/story.upcase.txt').first
145
+
146
+ book.attachment(:content).delete_all
147
+
148
+ assert_nil GRIDDB.fs.find(filename: 'grid-book/story.txt').first
149
+ assert_nil GRIDDB.fs.find(filename: 'grid-book/story.upcase.txt').first
150
+
151
+ file.close
152
+ file.unlink
153
+ end
154
+
155
+ it 'Deletes one attachment at a time' do
156
+ file = Tempfile.new('foo')
157
+
158
+ book = GridBook.new
159
+
160
+ field_value = book.attachment(:content).create({
161
+ tempfile: file,
162
+ filename: 'story.txt',
163
+ type: 'text/plain'
164
+ })
165
+ book.content = field_value
166
+
167
+ refute_nil GRIDDB.fs.find(filename: 'grid-book/story.txt').first
168
+ refute_nil GRIDDB.fs.find(filename: 'grid-book/story.upcase.txt').first
169
+
170
+ book.attachment(:content).delete
171
+
172
+ assert_nil GRIDDB.fs.find(filename: 'grid-book/story.txt').first
173
+ refute_nil GRIDDB.fs.find(filename: 'grid-book/story.upcase.txt').first
174
+
175
+ book.attachment(:content).delete :upcase
176
+
177
+ assert_nil GRIDDB.fs.find(filename: 'grid-book/story.upcase.txt').first
178
+
179
+ file.close
180
+ file.unlink
181
+ end
182
+
183
+ # Update
184
+
185
+ it 'Deletes previous attachment when saving a new one' do
186
+ file = Tempfile.new('foo')
187
+ file.write('hello')
188
+ file.rewind
189
+
190
+ book = GridBook.new
191
+
192
+ field_value = book.attachment(:content).create({
193
+ tempfile: file,
194
+ filename: 'story.txt',
195
+ type: 'text/plain'
196
+ })
197
+ book.content = field_value
198
+
199
+ refute_nil GRIDDB.fs.find(filename: 'grid-book/story.txt').first
200
+ refute_nil GRIDDB.fs.find(filename: 'grid-book/story.upcase.txt').first
201
+
202
+ file.rewind
203
+ file.write('world')
204
+ file.rewind
205
+
206
+ field_value = book.attachment(:content).create({
207
+ tempfile: file,
208
+ filename: 'history.md',
209
+ type: 'text/markdown'
210
+ })
211
+ book.content = field_value
212
+
213
+ assert_nil GRIDDB.fs.find(filename: 'grid-book/story.txt').first
214
+ assert_nil GRIDDB.fs.find(filename: 'grid-book/story.upcase.txt').first
215
+ refute_nil GRIDDB.fs.find(filename: 'grid-book/history.md').first
216
+ refute_nil GRIDDB.fs.find(filename: 'grid-book/history.upcase.txt').first
217
+
218
+ gridfile = GRIDDB.fs.find(filename: 'grid-book/history.md').first
219
+ assert_equal 'text/markdown', gridfile['contentType']
220
+ assert_equal 'grid-book', gridfile['metadata']['parent_collection']
221
+ GRIDDB.fs.open_download_stream(gridfile['_id']) do |stream|
222
+ assert_equal 'world', stream.read
223
+ end
224
+
225
+ gridfile = GRIDDB.fs.find(filename: 'grid-book/history.upcase.txt').first
226
+ assert_equal 'text/plain', gridfile['contentType']
227
+ assert_equal 'grid-book', gridfile['metadata']['parent_collection']
228
+ GRIDDB.fs.open_download_stream(gridfile['_id']) do |stream|
229
+ assert_equal 'WORLD', stream.read
230
+ end
231
+
232
+ file.close
233
+ file.unlink
234
+ end
235
+
236
+ end
237
+
238
+ GRIDDB.drop
239
+