populate-me 0.12.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 +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
|
+
|