resort-bjones 0.3.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.
@@ -0,0 +1,4 @@
1
+ module Resort
2
+ # Resort's version number
3
+ VERSION = "0.2.3"
4
+ end
@@ -0,0 +1,76 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "resort-bjones"
8
+ s.version = "0.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Oriol Gual", "Josep M. Bach", "Josep Jaume Rey", "Brian Jones"]
12
+ s.date = "2011-12-14"
13
+ s.description = "Positionless model sorting for Rails 3."
14
+ s.email = "cbj@gnu.org"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE"
17
+ ]
18
+ s.files = [
19
+ ".rspec",
20
+ ".rvmrc",
21
+ ".travis.yml",
22
+ "Gemfile",
23
+ "LICENSE",
24
+ "Rakefile",
25
+ "Readme.md",
26
+ "VERSION",
27
+ "lib/generators/active_record/resort_generator.rb",
28
+ "lib/generators/active_record/templates/migration.rb",
29
+ "lib/resort-bjones.rb",
30
+ "lib/resort.rb",
31
+ "lib/resort/version.rb",
32
+ "resort-bjones.gemspec",
33
+ "spec/generators/migration_spec.rb",
34
+ "spec/resort_spec.rb",
35
+ "spec/spec_helper.rb"
36
+ ]
37
+ s.homepage = "http://github.com/bjones/resort"
38
+ s.licenses = ["MIT"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = "1.8.10"
41
+ s.summary = "Positionless model sorting for Rails 3."
42
+
43
+ if s.respond_to? :specification_version then
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ s.add_runtime_dependency(%q<activerecord>, ["< 3.2", ">= 3.0.5"])
48
+ s.add_development_dependency(%q<jeweler>, [">= 1.5.2"])
49
+ s.add_development_dependency(%q<rake>, [">= 0"])
50
+ s.add_development_dependency(%q<sqlite3>, [">= 0"])
51
+ s.add_development_dependency(%q<rspec>, [">= 0"])
52
+ s.add_development_dependency(%q<yard>, [">= 0"])
53
+ s.add_development_dependency(%q<bluecloth>, [">= 0"])
54
+ s.add_development_dependency(%q<generator_spec>, ["~> 0.8.1"])
55
+ else
56
+ s.add_dependency(%q<activerecord>, ["< 3.2", ">= 3.0.5"])
57
+ s.add_dependency(%q<jeweler>, [">= 1.5.2"])
58
+ s.add_dependency(%q<rake>, [">= 0"])
59
+ s.add_dependency(%q<sqlite3>, [">= 0"])
60
+ s.add_dependency(%q<rspec>, [">= 0"])
61
+ s.add_dependency(%q<yard>, [">= 0"])
62
+ s.add_dependency(%q<bluecloth>, [">= 0"])
63
+ s.add_dependency(%q<generator_spec>, ["~> 0.8.1"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<activerecord>, ["< 3.2", ">= 3.0.5"])
67
+ s.add_dependency(%q<jeweler>, [">= 1.5.2"])
68
+ s.add_dependency(%q<rake>, [">= 0"])
69
+ s.add_dependency(%q<sqlite3>, [">= 0"])
70
+ s.add_dependency(%q<rspec>, [">= 0"])
71
+ s.add_dependency(%q<yard>, [">= 0"])
72
+ s.add_dependency(%q<bluecloth>, [">= 0"])
73
+ s.add_dependency(%q<generator_spec>, ["~> 0.8.1"])
74
+ end
75
+ end
76
+
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'action_controller'
3
+ require 'action_view'
4
+ ActionView::Template::Handlers::ERB::ENCODING_FLAG = ActionView::ENCODING_FLAG
5
+ require 'generator_spec/test_case'
6
+
7
+ module Resort
8
+ module Generators
9
+ describe MigrationGenerator do
10
+ include GeneratorSpec::TestCase
11
+ destination File.expand_path('../../../tmp', __FILE__)
12
+ tests MigrationGenerator
13
+ arguments %w(article)
14
+
15
+ before(:all) do
16
+ prepare_destination
17
+ mkdir File.join(self.test_case.destination_root, 'config')
18
+ run_generator
19
+ end
20
+
21
+ it 'generates Resort migration' do
22
+ destination_root.should have_structure {
23
+
24
+ directory "db" do
25
+ directory "migrate" do
26
+ migration "add_resort_fields_to_articles" do
27
+ contains "class AddResortFieldsToArticles"
28
+ contains ":articles, :next_id"
29
+ contains ":articles, :first"
30
+ end
31
+ end
32
+ end
33
+
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,447 @@
1
+ require 'spec_helper'
2
+
3
+ module Resort
4
+ describe Sortable do
5
+
6
+ subject { Article.new }
7
+
8
+ context 'when included' do
9
+ it 'creates previous and next relationships' do
10
+ subject.should respond_to(:previous, :next)
11
+ end
12
+
13
+ it 'includes base with InstanceMethods' do
14
+ subject.class.ancestors.should include(Sortable::InstanceMethods)
15
+ end
16
+ it 'extend base with ClassMethods' do
17
+ (class << subject.class; self; end).ancestors.should include(Sortable::ClassMethods)
18
+ end
19
+ it 'defines a siblings method' do
20
+ subject.should respond_to(:siblings)
21
+ end
22
+ end
23
+
24
+ describe 'ClassMethods' do
25
+
26
+ describe "ordering" do
27
+ before do
28
+ Article.destroy_all
29
+
30
+ 4.times do |i|
31
+ Article.create(:name => i.to_s)
32
+ end
33
+
34
+ Article.find_by_name('0').append_to(Article.find_by_name('3'))
35
+ Article.find_by_name('1').append_to(Article.find_by_name('3'))
36
+ Article.find_by_name('2').append_to(Article.find_by_name('3'))
37
+
38
+ @article1 = Article.find_by_name('3')
39
+ @article2 = Article.find_by_name('2')
40
+ @article3 = Article.find_by_name('1')
41
+ @article4 = Article.find_by_name('0')
42
+ end
43
+
44
+ describe "#first_in_order" do
45
+ it 'returns the first element of the list' do
46
+ Article.first_in_order.should == @article1
47
+ end
48
+ end
49
+
50
+ describe "#last_in_order" do
51
+ it 'returns the last element of the list' do
52
+ Article.last_in_order.should == @article4
53
+ end
54
+ end
55
+
56
+ describe "#ordered" do
57
+ it 'returns all elements ordered' do
58
+ Article.ordered.should == [@article1, @article2, @article3, @article4]
59
+ end
60
+ end
61
+
62
+ after do
63
+ Article.destroy_all
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "siblings" do
69
+ before do
70
+ one_list = OrderedList.create(:name => 'My list')
71
+ another_list = OrderedList.create(:name => 'My other list')
72
+
73
+ 4.times do |i|
74
+ one_list.items << ListItem.new(:name => "My list item #{i}")
75
+ another_list.items << ListItem.new(:name => "My other list item #{i}")
76
+ end
77
+
78
+ end
79
+
80
+ describe "#first_in_order" do
81
+ it 'returns the first element of the list' do
82
+ OrderedList.find_by_name('My list').items.first_in_order.name.should == "My list item 0"
83
+ OrderedList.find_by_name('My other list').items.first_in_order.name.should == "My other list item 0"
84
+ end
85
+ end
86
+
87
+ describe "#last_in_order" do
88
+ it 'returns the last element of the list' do
89
+ OrderedList.find_by_name('My list').items.last_in_order.name.should == "My list item 3"
90
+ OrderedList.find_by_name('My other list').items.last_in_order.name.should == "My other list item 3"
91
+ end
92
+ end
93
+
94
+ describe "#ordered" do
95
+ it 'returns all elements ordered' do
96
+ OrderedList.find_by_name('My list').items.ordered.map(&:name).should == ['My list item 0', 'My list item 1', 'My list item 2', 'My list item 3']
97
+ OrderedList.find_by_name('My other list').items.ordered.map(&:name).should == ['My other list item 0', 'My other list item 1', 'My other list item 2', 'My other list item 3']
98
+ end
99
+
100
+ it 'raises when ordering without scope' do
101
+ expect {
102
+ ListItem.ordered
103
+ }.to raise_error
104
+ end
105
+ end
106
+
107
+ after do
108
+ OrderedList.destroy_all
109
+ ListItem.destroy_all
110
+ end
111
+ end
112
+
113
+ describe "after create" do
114
+ context 'when there are no siblings' do
115
+ it 'prepends the element' do
116
+ article = Article.create(:name => 'first!')
117
+
118
+ article.should be_first
119
+ article.next.should be_nil
120
+ article.previous.should be_nil
121
+ end
122
+ end
123
+ context 'otherwise' do
124
+ it 'appends the element' do
125
+ Article.create(:name => "1")
126
+ Article.create(:name => 'last!')
127
+
128
+ article = Article.find_by_name('last!')
129
+ first = Article.find_by_name('1')
130
+
131
+ article.should be_last
132
+ article.next_id.should be_nil
133
+ article.previous.name.should == '1'
134
+
135
+ first.next_id.should eq(article.id)
136
+ end
137
+ end
138
+ after do
139
+ Article.destroy_all
140
+ end
141
+
142
+ context "with custom siblings" do
143
+
144
+ context 'when there are no siblings' do
145
+ it 'prepends the element' do
146
+ one_list = OrderedList.create(:name => 'My list')
147
+ another_list = OrderedList.create(:name => 'My other list')
148
+ item = ListItem.create(:name => "My list item", :ordered_list => one_list)
149
+
150
+ item.should be_first
151
+ item.next.should be_nil
152
+ item.previous.should be_nil
153
+ end
154
+ end
155
+ context 'otherwise' do
156
+ it 'appends the element' do
157
+ one_list = OrderedList.create(:name => 'My list')
158
+ another_list = OrderedList.create(:name => 'My other list')
159
+ ListItem.create(:name => "1", :ordered_list => one_list)
160
+ ListItem.create(:name => "last!", :ordered_list => one_list)
161
+
162
+ first = ListItem.find_by_name('1')
163
+ last = ListItem.find_by_name('last!')
164
+
165
+ last.should be_last
166
+ last.next_id.should be_nil
167
+ last.previous.name.should == '1'
168
+
169
+ first.next_id.should eq(last.id)
170
+ end
171
+
172
+ it 'prepends the last element' do
173
+ one_list = OrderedList.create(:name => 'My list')
174
+ ListItem.create(:name => "First", :ordered_list => one_list)
175
+ ListItem.create(:name => "Second", :ordered_list => one_list)
176
+ third = ListItem.create(:name => "Third", :ordered_list => one_list)
177
+
178
+ third.prepend
179
+ first = ListItem.where(:name => "First", :ordered_list_id => one_list).first
180
+ second = ListItem.where(:name => "Second", :ordered_list_id => one_list).first
181
+ third = ListItem.where(:name => "Third", :ordered_list_id => one_list).first
182
+
183
+ first.should_not be_first
184
+ second.should_not be_first
185
+ third.should be_first
186
+ third.next.name.should == 'First'
187
+ first.next.name.should == 'Second'
188
+ second.next.should be_nil
189
+ end
190
+
191
+ end
192
+ after do
193
+ OrderedList.destroy_all
194
+ ListItem.destroy_all
195
+ end
196
+ end
197
+ end
198
+
199
+ describe "after destroy" do
200
+ context 'when the element is the first' do
201
+ it 'removes the element' do
202
+ article = Article.create(:name => 'first!')
203
+ article2 = Article.create(:name => 'second!')
204
+ article3 = Article.create(:name => 'last!')
205
+
206
+ article = Article.find_by_name('first!')
207
+ article.destroy
208
+
209
+ article2 = Article.find_by_name('second!')
210
+
211
+ article2.should be_first
212
+ article2.previous.should be_nil
213
+ end
214
+ end
215
+ context 'when the element is in the middle' do
216
+ it 'removes the element' do
217
+ article = Article.create(:name => 'first!')
218
+ article2 = Article.create(:name => 'second!')
219
+ article3 = Article.create(:name => 'last!')
220
+
221
+ article = Article.find_by_name('first!')
222
+
223
+ article2 = Article.find_by_name('second!')
224
+ article2.destroy
225
+
226
+ article = Article.find_by_name('first!')
227
+ article3 = Article.find_by_name('last!')
228
+
229
+ article.should be_first
230
+ article.next.name.should == 'last!'
231
+ article3.previous.name.should == 'first!'
232
+ end
233
+ end
234
+ context 'when the element is last' do
235
+ it 'removes the element' do
236
+ article = Article.create(:name => 'first!')
237
+ article2 = Article.create(:name => 'second!')
238
+ article3 = Article.create(:name => 'last!')
239
+
240
+ article3.destroy
241
+
242
+ article2.next.should be_nil
243
+ end
244
+ end
245
+ after do
246
+ Article.destroy_all
247
+ end
248
+ end
249
+
250
+ describe 'InstanceMethods' do
251
+ before do
252
+ Article.destroy_all
253
+ Article.create(:name => "1")
254
+ Article.create(:name => "2")
255
+ Article.create(:name => "3")
256
+ Article.create(:name => "4")
257
+
258
+ @article1 = Article.find_by_name('1')
259
+ @article2 = Article.find_by_name('2')
260
+ @article3 = Article.find_by_name('3')
261
+ @article4 = Article.find_by_name('4')
262
+ end
263
+
264
+ describe "#push" do
265
+ it "appends the element to the list" do
266
+ @article1.push
267
+
268
+ article1 = Article.find_by_name('1')
269
+ article1.previous.should == @article4
270
+ article1.next.should be_nil
271
+ end
272
+ context 'when the article is already last' do
273
+ it 'does nothing' do
274
+ @article4.push
275
+
276
+ @article4.previous.name.should == '3'
277
+ @article4.next.should be_nil
278
+ end
279
+ end
280
+ end
281
+
282
+ describe "#prepend" do
283
+ it "prepends the element" do
284
+ @article3.prepend
285
+
286
+ article3 = Article.find_by_name('3')
287
+
288
+ article3.should be_first
289
+ article3.previous.should be_nil
290
+ article3.next.name.should == '1'
291
+ end
292
+
293
+ it "prepends the last element" do
294
+ @article4.prepend
295
+
296
+ article4 = Article.find_by_name('4')
297
+
298
+ article4.should be_first
299
+ article4.previous.should be_nil
300
+ article4.next.name.should == '1'
301
+ end
302
+
303
+ it 'will raise ActiveRecord::RecordNotSaved if update fails' do
304
+ @article2.should_receive(:update_attribute).and_return(false)
305
+ expect { @article2.prepend }.to raise_error(ActiveRecord::RecordNotSaved)
306
+ end
307
+
308
+ context 'when the article is already first' do
309
+ it 'does nothing' do
310
+ @article1.prepend
311
+
312
+ @article1.previous.should be_nil
313
+ @article1.next.name.should == '2'
314
+ end
315
+ end
316
+ end
317
+
318
+ describe "#append_to" do
319
+ it 'will raise ActiveRecord::RecordNotSaved if update fails' do
320
+ @article2.should_receive(:update_attribute).and_return(false)
321
+ expect { @article2.append_to(@article3) }.to raise_error(ActiveRecord::RecordNotSaved)
322
+ end
323
+
324
+ context 'appending 1 after 2' do
325
+ it "appends the element after another element" do
326
+ @article1.append_to(@article2)
327
+
328
+ article1 = Article.find_by_name('1')
329
+ article1.next.name.should == '3'
330
+ article1.previous.name.should == '2'
331
+ @article3.previous.name.should == '1'
332
+ end
333
+
334
+ it "sets the other element as first" do
335
+ @article1.append_to(@article2)
336
+
337
+ article2 = Article.find_by_name('2')
338
+ article2.next.name.should == '1'
339
+ article2.should be_first
340
+ end
341
+ end
342
+
343
+ context 'appending 1 after 3' do
344
+ it "appends the element after another element" do
345
+ @article1.append_to(@article3)
346
+
347
+ article1 = Article.find_by_name('1')
348
+ article1.should_not be_first
349
+ article1.previous.name.should == '3'
350
+ article1.next.name.should == '4'
351
+
352
+ @article3.next.name.should == '1'
353
+ @article4.previous.name.should == '1'
354
+ end
355
+
356
+ it 'resets the first element' do
357
+ @article1.append_to(@article3)
358
+
359
+ article2 = Article.find_by_name('2')
360
+ article2.should be_first
361
+ article2.previous.should be_nil
362
+ end
363
+ end
364
+
365
+ context 'appending 2 after 3' do
366
+ it "appends the element after another element" do
367
+ @article2.append_to(@article3)
368
+
369
+ article1 = Article.find_by_name('1')
370
+ article1.next.name.should == '3'
371
+
372
+ article2 = Article.find_by_name('2')
373
+ article2.previous.name.should == '3'
374
+ article2.next.name.should == '4'
375
+
376
+ @article3.previous.name.should == '1'
377
+ @article3.next.name.should == '2'
378
+
379
+ @article4.previous.name.should == '2'
380
+ end
381
+ end
382
+ context 'appending 2 after 4' do
383
+ it "appends the element after another element" do
384
+ @article2.append_to(@article4)
385
+
386
+ article1 = Article.find_by_name('1')
387
+ article3 = Article.find_by_name('3')
388
+
389
+ article1.next.name.should == '3'
390
+ article3.previous.name.should == '1'
391
+
392
+ article2 = Article.find_by_name('2')
393
+ article2.previous.name.should == '4'
394
+ article2.should be_last
395
+
396
+ @article4.next.name.should == '2'
397
+ end
398
+ end
399
+ context 'appending 4 after 2' do
400
+ it "appends the element after another element" do
401
+ @article4.append_to(@article2)
402
+
403
+ article3 = Article.find_by_name('3')
404
+ article3.next.should be_nil
405
+ article3.previous.name.should == '4'
406
+
407
+ article4 = Article.find_by_name('4')
408
+ @article2.next.name.should == '4'
409
+ article4.previous.name.should == '2'
410
+ article4.next.name.should == '3'
411
+ end
412
+ end
413
+ context 'appending 3 after 1' do
414
+ it "appends the element after another element" do
415
+ @article3.append_to(@article1)
416
+
417
+ article1 = Article.find_by_name('1')
418
+ article1.next.name.should == '3'
419
+
420
+ article2 = Article.find_by_name('2')
421
+ article2.previous.name.should == '3'
422
+ article2.next.name.should == '4'
423
+
424
+ article3 = Article.find_by_name('3')
425
+ article3.previous.name.should == '1'
426
+ article3.next.name.should == '2'
427
+
428
+ @article4.previous.name.should == '2'
429
+ end
430
+ end
431
+
432
+ context 'when the article is already after the other element' do
433
+ it 'does nothing' do
434
+ @article2.append_to(@article1)
435
+
436
+ article1 = Article.find_by_name('1')
437
+ article2 = Article.find_by_name('2')
438
+
439
+ article1.next.name.should == '2'
440
+ article2.previous.name.should == '1'
441
+ end
442
+ end
443
+ end
444
+ end
445
+
446
+ end
447
+ end