populate-me 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +655 -0
- data/Rakefile +14 -0
- data/example/config.ru +100 -0
- data/lib/populate_me.rb +2 -0
- data/lib/populate_me/admin.rb +157 -0
- data/lib/populate_me/admin/__assets__/css/asmselect.css +63 -0
- data/lib/populate_me/admin/__assets__/css/jquery-ui.min.css +6 -0
- data/lib/populate_me/admin/__assets__/css/main.css +244 -0
- data/lib/populate_me/admin/__assets__/img/help/children.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/create.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/delete.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/edit.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/form.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/list.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/login.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/logout.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/menu.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/overview.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/save.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/sort.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/sublist.png +0 -0
- data/lib/populate_me/admin/__assets__/js/asmselect.js +412 -0
- data/lib/populate_me/admin/__assets__/js/columnav.js +87 -0
- data/lib/populate_me/admin/__assets__/js/jquery-ui.min.js +7 -0
- data/lib/populate_me/admin/__assets__/js/main.js +388 -0
- data/lib/populate_me/admin/__assets__/js/mustache.js +578 -0
- data/lib/populate_me/admin/__assets__/js/sortable.js +2 -0
- data/lib/populate_me/admin/views/help.erb +94 -0
- data/lib/populate_me/admin/views/page.erb +189 -0
- data/lib/populate_me/api.rb +124 -0
- data/lib/populate_me/attachment.rb +186 -0
- data/lib/populate_me/document.rb +192 -0
- data/lib/populate_me/document_mixins/admin_adapter.rb +149 -0
- data/lib/populate_me/document_mixins/callbacks.rb +125 -0
- data/lib/populate_me/document_mixins/outcasting.rb +83 -0
- data/lib/populate_me/document_mixins/persistence.rb +95 -0
- data/lib/populate_me/document_mixins/schema.rb +198 -0
- data/lib/populate_me/document_mixins/typecasting.rb +70 -0
- data/lib/populate_me/document_mixins/validation.rb +44 -0
- data/lib/populate_me/file_system_attachment.rb +40 -0
- data/lib/populate_me/grid_fs_attachment.rb +103 -0
- data/lib/populate_me/mongo.rb +160 -0
- data/lib/populate_me/s3_attachment.rb +120 -0
- data/lib/populate_me/variation.rb +38 -0
- data/lib/populate_me/version.rb +4 -0
- data/populate-me.gemspec +34 -0
- data/test/helper.rb +37 -0
- data/test/test_admin.rb +183 -0
- data/test/test_api.rb +246 -0
- data/test/test_attachment.rb +167 -0
- data/test/test_document.rb +128 -0
- data/test/test_document_admin_adapter.rb +221 -0
- data/test/test_document_callbacks.rb +151 -0
- data/test/test_document_outcasting.rb +247 -0
- data/test/test_document_persistence.rb +83 -0
- data/test/test_document_schema.rb +280 -0
- data/test/test_document_typecasting.rb +128 -0
- data/test/test_grid_fs_attachment.rb +239 -0
- data/test/test_mongo.rb +324 -0
- data/test/test_s3_attachment.rb +281 -0
- data/test/test_variation.rb +91 -0
- data/test/test_version.rb +11 -0
- 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
|
+
|