rankle 0.0.0.pre → 0.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c1c94e6dadbdd8d36db346f7767bc847d9402d4
4
- data.tar.gz: 5923e83360576985bc983215e94041d6c53d2906
3
+ metadata.gz: 93e91d72d1ae47471f1be951fdf49bea1b5de6c0
4
+ data.tar.gz: e6cee95ce83201559b16638e08eefb73b1cf89ba
5
5
  SHA512:
6
- metadata.gz: 513aa6cd74b4a39425e66cd19f5ed7980523dee33b4bdd15ea46d5c8dcfc3836f5c3bec7215db8249daec2cc843192d011fb4a7b2e25c74ba58bb70d38cf48a3
7
- data.tar.gz: d244dd513e0a1ff12c339f32d21a9d16bb882b8002ecd452382b7bc389ec217bbd1e84a4bd00166d346a50bf4633d9a4a18aff9d86e28717f04e3385d6d6639f
6
+ metadata.gz: 196913721f0a56bd267fc1f1b17c13e8e4a3dde6251fb22c3a6ffecc4896bb510a9d24ecceb73712d53f50e5a26ed52b699a1fb988a392949a0813020da59f00
7
+ data.tar.gz: e3919e144ebd2a1d86f4a42a0105c7bd946567e6caef4061e7d2bd75fb29266c50ed0b4fc3065ee10bb1d6766a380a3300578cdf41893fb58609f54f1c41b79d
data/.gitignore CHANGED
@@ -34,3 +34,6 @@ Gemfile.lock
34
34
 
35
35
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
36
36
  .rvmrc
37
+
38
+ # The db directory is used by the test suite to verify the rankle install generator
39
+ /db
data/README.md CHANGED
@@ -1,10 +1,7 @@
1
- # Rankle (pre-release)
1
+ # Rankle
2
2
 
3
3
  Rankle provides multi-resource ranking. It uses a separate join table rather than a resource specific position column.
4
4
 
5
- ** Rankle is currently in a pre-release state. It is not optimized for performance and should not be used in
6
- production applications. Future work will be tracked with issues. **
7
-
8
5
  ## Installation
9
6
 
10
7
  Add this line to your application's Gemfile:
@@ -55,8 +52,8 @@ orange.position # 1
55
52
  The ranked method provides an ordered ActiveRecord::Relation:
56
53
 
57
54
  ```ruby
58
- Fruit.create! name, 'apple'
59
- Fruit.create! name, 'orange'
55
+ Fruit.create! name: 'apple'
56
+ Fruit.create! name: 'orange'
60
57
 
61
58
  Fruit.ranked.map(&:name) # ['apple', 'orange']
62
59
  ```
@@ -104,43 +101,43 @@ Fruit.ranked.map(&:name) # ['apple', 'banana', 'orange']
104
101
  Passing a symbol to the rank method with a position will update the position to that named rank:
105
102
 
106
103
  ```ruby
107
- apple = Fruit.create!
108
- orange = Fruit.create!
104
+ apple = Fruit.create! name: 'apple'
105
+ orange = Fruit.create! name: 'orange'
109
106
 
110
- apple.rank :reverse, 1
111
- orange.rank :reverse, 0
107
+ apple.rank :reverse, 1
108
+ orange.rank :reverse, 0
112
109
 
113
- apple.position # 0
114
- orange.position # 1
110
+ apple.position # 0
111
+ orange.position # 1
115
112
 
116
- apple.position :reverse # 1
117
- orange.position :reverse # 0
113
+ apple.position :reverse # 1
114
+ orange.position :reverse # 0
118
115
 
119
- Fruit.ranked.map(&:name) # ['apple', 'orange']
120
- Fruit.ranked(:reverse).map(&:name) # ['orange', 'apple']
116
+ Fruit.ranked.map(&:name) # ['apple', 'orange']
117
+ Fruit.ranked(:reverse).map(&:name) # ['orange', 'apple']
121
118
  ```
122
119
 
123
120
  Since positions are not stored with an absolute value, the available positions increases by 1 with each call to the rank method:
124
121
 
125
122
  ```ruby
126
- apple = Fruit.create!
127
- banana = Fruit.create!
128
- orange = Fruit.create!
123
+ apple = Fruit.create! name: 'apple'
124
+ banana = Fruit.create! name: 'banana'
125
+ orange = Fruit.create! name: 'orange'
129
126
 
130
- apple.rank :reverse, 2 # [apple]
131
- banana.rank :reverse, 1 # [banana, apple]
132
- orange.rank :reverse, 0 # [orange, banana, apple]
127
+ apple.rank :reverse, 2 # [apple]
128
+ banana.rank :reverse, 1 # [banana, apple]
129
+ orange.rank :reverse, 0 # [orange, banana, apple]
133
130
 
134
- apple.position # 0
135
- banana.position # 1
136
- orange.position # 2
131
+ apple.position # 0
132
+ banana.position # 1
133
+ orange.position # 2
137
134
 
138
- apple.position :reverse # 1
139
- banana.position :reverse # 2
140
- orange.position :reverse # 0
135
+ apple.position :reverse # 1
136
+ banana.position :reverse # 2
137
+ orange.position :reverse # 0
141
138
 
142
- Fruit.ranked.map(&:name) # ['apple', 'banana', 'orange']
143
- Fruit.ranked(:reverse).map(&:name) # ['orange', 'apple', 'banana']
139
+ Fruit.ranked.map(&:name) # ['apple', 'banana', 'orange']
140
+ Fruit.ranked(:reverse).map(&:name) # ['orange', 'apple', 'banana']
144
141
  ```
145
142
 
146
143
  You can bypass this issue by registering the ranking on the class:
@@ -150,9 +147,9 @@ class Fruit < ActiveRecord::Base
150
147
  ranks :reverse
151
148
  end
152
149
 
153
- apple = Fruit.create!
154
- banana = Fruit.create!
155
- orange = Fruit.create!
150
+ apple = Fruit.create! name: 'apple'
151
+ banana = Fruit.create! name: 'banana'
152
+ orange = Fruit.create! name: 'orange'
156
153
 
157
154
  apple.position # 0
158
155
  banana.position # 1
@@ -189,8 +186,8 @@ end
189
186
  class Vegetable < ActiveRecord::Base
190
187
  end
191
188
 
192
- apple = Fruit.create!
193
- carrot = Vegetable.create!
189
+ apple = Fruit.create! name: 'apple'
190
+ carrot = Vegetable.create! name: 'carrot'
194
191
 
195
192
  apple.rank :produce, 0
196
193
  carrot.rank :produce, 1
@@ -227,8 +224,8 @@ Class Vegetable
227
224
  ranks :produce
228
225
  end
229
226
 
230
- apple = Fruit.create!
231
- carrot = Vegetable.create!
227
+ apple = Fruit.create! name: 'apple'
228
+ carrot = Vegetable.create! name: 'vegetable'
232
229
 
233
230
  apple.position # 0
234
231
  carrot.position # 0
@@ -237,6 +234,29 @@ apple.position :produce # 0
237
234
  carrot.position :produce # 1
238
235
  ```
239
236
 
237
+ ## Scoped Ranking
238
+
239
+ ActiveRecord scopes work in conjunction to further restrict the ranking:
240
+
241
+ ```ruby
242
+ class Fruit
243
+ scope :berries, -> { where "name LIKE ?", '%berry' }
244
+ end
245
+
246
+ Fruit.create! name: 'apple'
247
+ Fruit.create! name: 'apricot'
248
+ Fruit.create! name: 'banana'
249
+ Fruit.create! name: 'bilberry'
250
+ Fruit.create! name: 'blackberry'
251
+ Fruit.create! name: 'blackcurrant'
252
+ Fruit.create! name: 'blueberry'
253
+ Fruit.create! name: 'boysenberry'
254
+ Fruit.create! name: 'cantaloupe'
255
+
256
+ Fruit.ranked.map(&:name) # ['apple', 'apricot', 'banana', 'bilberry', 'blackberry', 'blackcurrant', 'blueberry', 'boysenberry', 'cantaloupe']
257
+ Fruit.berries.ranked.map(&:name) # ['bilberry', 'blackberry', 'blueberry', 'boysenberry']
258
+ ```
259
+
240
260
  ## Contributing
241
261
 
242
262
  1. Fork it ( https://github.com/[my-github-username]/rankle/fork )
data/Rakefile CHANGED
@@ -1,14 +1,14 @@
1
1
  require 'bundler/gem_tasks'
2
- require 'cucumber'
3
- require 'cucumber/rake/task'
4
2
  require 'yard'
3
+ require 'rake/testtask'
5
4
 
6
- Cucumber::Rake::Task.new(:features) do |t|
7
- t.cucumber_opts = 'features --format pretty'
5
+ Rake::TestTask.new do |t|
6
+ t.test_files = FileList['test/*_test.rb']
7
+ t.verbose = true
8
8
  end
9
9
 
10
10
  YARD::Rake::YardocTask.new do |t|
11
11
  t.files = ['lib/rankle.rb']
12
12
  end
13
13
 
14
- task :default => [:features, :yard]
14
+ task :default => [:test, :yard]
@@ -7,7 +7,7 @@ module Rankle
7
7
  source_root File.expand_path('../templates/', __FILE__)
8
8
 
9
9
  def generate_migration
10
- migration_template 'migration.rb', 'db/migrate/create_rankle_index.rb'
10
+ migration_template 'migration.rb', 'db/migrate/create_rankle_indices.rb'
11
11
  end
12
12
 
13
13
  def self.next_migration_number(dir)
@@ -1,6 +1,6 @@
1
- class CreateRankleIndex < ActiveRecord::Migration
1
+ class CreateRankleIndices < ActiveRecord::Migration
2
2
  def change
3
- create_table(:rankle_index) do |t|
3
+ create_table :rankle_indices do |t|
4
4
  t.string :indexable_name
5
5
  t.integer :indexable_id
6
6
  t.string :indexable_type
@@ -9,9 +9,9 @@ class CreateRankleIndex < ActiveRecord::Migration
9
9
  t.timestamps null: false
10
10
  end
11
11
 
12
- add_index :rankle_index, :indexable_name
13
- add_index :rankle_index, :indexable_id
14
- add_index :rankle_index, :indexable_type
15
- add_index :rankle_index, :indexable_position
12
+ add_index :rankle_indices, :indexable_name
13
+ add_index :rankle_indices, :indexable_id
14
+ add_index :rankle_indices, :indexable_type
15
+ add_index :rankle_indices, :indexable_position
16
16
  end
17
17
  end
data/lib/rankle/ranker.rb CHANGED
@@ -1,18 +1,9 @@
1
1
  module Rankle
2
2
  class Ranker
3
- def self.put klass, proc
4
- @rankers ||= {}
5
- @rankers[klass] = proc
6
- end
7
-
8
- def self.get klass
9
- @rankers[klass] rescue nil
10
- end
3
+ attr_accessor :strategy
11
4
 
12
- def self.swap(first_index, second_index)
13
- first_index_position = first_index.indexable_position
14
- first_index.update_attribute(:indexable_position, second_index.indexable_position)
15
- second_index.update_attribute(:indexable_position, first_index_position)
5
+ def initialize strategy
6
+ @strategy = strategy
16
7
  end
17
8
  end
18
9
  end
@@ -1,3 +1,3 @@
1
1
  module Rankle
2
- VERSION = '0.0.0.pre'
2
+ VERSION = '0.0.0'
3
3
  end
data/lib/rankle.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'active_record'
2
+ require 'rankle_index'
2
3
  require 'rankle/ranker'
3
4
  require 'rankle/version'
4
5
 
@@ -22,28 +23,19 @@ module Rankle
22
23
  end
23
24
  end
24
25
 
25
- def ranks proc
26
- Ranker.put self, proc
26
+ def ranks strategy
27
+ RankleIndex.ranks self, Ranker.new(strategy)
27
28
  end
28
29
  end
29
30
 
30
31
  def self.ranked name = :default
31
- RankleIndex.where(indexable_name: name).order(:indexable_position).map do |duck|
32
- duck.indexable_type.classify.constantize.find(duck.indexable_id)
33
- end
32
+ RankleIndex.ranked name
34
33
  end
35
34
 
36
35
  # instance methods added to ActiveRecord models
37
36
  module InstanceMethods
38
37
  def set_default_position
39
- if Ranker.get(self.class)
40
- position = self.class.ranked.each_with_index { |record, index| break index if Ranker.get(self.class).call(self, record) }
41
- end unless Ranker.get(self.class).is_a?(Symbol)
42
- position = self.class.count - 1 if position.nil? || position.is_a?(Array)
43
- rank position
44
- if Ranker.get(self.class).is_a?(Symbol)
45
- rank Ranker.get(self.class), RankleIndex.where(indexable_name: Ranker.get(self.class)).count
46
- end
38
+ RankleIndex.set_default_position self
47
39
  end
48
40
 
49
41
  # Assigns an explicit position to the record
@@ -55,28 +47,11 @@ module Rankle
55
47
  end
56
48
 
57
49
  def rank name = :default, position
58
- rankle_index = RankleIndex.where(indexable_name: name.to_s, indexable_id: id, indexable_type: self.class).first_or_create!
59
- rankle_index_length = if name == :default
60
- RankleIndex.where(indexable_name: name.to_s, indexable_type: self.class).count
61
- else
62
- RankleIndex.where(indexable_name: name.to_s).count
63
- end
64
- position = 0 if position < 0
65
- position = rankle_index_length - 1 if position >= rankle_index_length
66
- rankle_index.update_attribute(:indexable_position, rankle_index_length - 1) unless rankle_index.indexable_position
67
- swap_distance = -1
68
- swap_distance *= -1 if rankle_index.indexable_position < position
69
- until rankle_index.indexable_position == position
70
- if name == :default
71
- Ranker.swap(rankle_index, RankleIndex.where(indexable_name: name.to_s, indexable_type: self.class, indexable_position: rankle_index.indexable_position + swap_distance).first)
72
- else
73
- Ranker.swap(rankle_index, RankleIndex.where(indexable_name: name.to_s, indexable_position: rankle_index.indexable_position + swap_distance).first)
74
- end
75
- end
50
+ RankleIndex.rank self, name, position
76
51
  end
77
52
 
78
53
  def position name = :default
79
- RankleIndex.where(indexable_name: name.to_s, indexable_id: id, indexable_type: self.class).first_or_create.indexable_position
54
+ RankleIndex.position self, name
80
55
  end
81
56
  end
82
57
  end
@@ -0,0 +1,57 @@
1
+ class RankleIndex < ActiveRecord::Base
2
+ belongs_to :indexable, polymorphic: true
3
+
4
+ @rankers = {}
5
+
6
+ def self.ranks klass, ranker
7
+ @rankers[klass] = ranker
8
+ end
9
+
10
+ def self.set_default_position instance
11
+ if @rankers[instance.class] && @rankers[instance.class].strategy
12
+ position = instance.class.ranked.each_with_index { |record, index| break index if @rankers[instance.class].strategy.call(instance, record) } unless @rankers[instance.class].strategy.is_a?(Symbol)
13
+ end
14
+ position = instance.class.count - 1 if position.nil? || position.is_a?(Array)
15
+ instance.rank position
16
+ if @rankers[instance.class] && @rankers[instance.class].strategy.is_a?(Symbol)
17
+ instance.rank @rankers[instance.class].strategy, RankleIndex.where(indexable_name: @rankers[instance.class].strategy).count
18
+ end
19
+ end
20
+
21
+ def self.rank instance, name, position
22
+ rankle_index = RankleIndex.where(indexable_name: name.to_s, indexable_id: instance.id, indexable_type: instance.class).first_or_create!
23
+ rankle_index_length = if name == :default
24
+ RankleIndex.where(indexable_name: name.to_s, indexable_type: instance.class).count
25
+ else
26
+ RankleIndex.where(indexable_name: name.to_s).count
27
+ end
28
+ position = 0 if position < 0
29
+ position = rankle_index_length - 1 if position >= rankle_index_length
30
+ rankle_index.update_attribute(:indexable_position, rankle_index_length - 1) unless rankle_index.indexable_position
31
+ swap_distance = -1
32
+ swap_distance *= -1 if rankle_index.indexable_position < position
33
+ until rankle_index.indexable_position == position
34
+ if name == :default
35
+ swap(rankle_index, RankleIndex.where(indexable_name: name.to_s, indexable_type: instance.class, indexable_position: rankle_index.indexable_position + swap_distance).first)
36
+ else
37
+ swap(rankle_index, RankleIndex.where(indexable_name: name.to_s, indexable_position: rankle_index.indexable_position + swap_distance).first)
38
+ end
39
+ end
40
+ end
41
+
42
+ def self.position instance, name
43
+ where(indexable_name: name.to_s, indexable_id: instance.id, indexable_type: instance.class).first_or_create!.indexable_position
44
+ end
45
+
46
+ def self.ranked name
47
+ where(indexable_name: name).order(:indexable_position).map do |duck|
48
+ duck.indexable_type.classify.constantize.find(duck.indexable_id)
49
+ end
50
+ end
51
+
52
+ def self.swap(first_index, second_index)
53
+ first_index_position = first_index.indexable_position
54
+ first_index.update_attribute(:indexable_position, second_index.indexable_position)
55
+ second_index.update_attribute(:indexable_position, first_index_position)
56
+ end
57
+ end
data/rankle.gemspec CHANGED
@@ -21,7 +21,6 @@ Gem::Specification.new do |spec|
21
21
  spec.add_dependency "activerecord"
22
22
  spec.add_development_dependency "bundler", "~> 1.7"
23
23
  spec.add_development_dependency "rake", "~> 10.0"
24
- spec.add_development_dependency "cucumber"
25
24
  spec.add_development_dependency "sqlite3"
26
25
  spec.add_development_dependency "rspec-expectations"
27
26
  spec.add_development_dependency "database_cleaner"
@@ -0,0 +1,22 @@
1
+ require_relative './support/test_helper'
2
+
3
+ class TestDefaultBehavior < Minitest::Test
4
+ def test_it_is_ineffectual
5
+ assert(Fruit.all.to_a == Fruit.ranked.to_a)
6
+ end
7
+
8
+ def test_it_sets_position
9
+ apple = Fruit.create!
10
+ orange = Fruit.create!
11
+
12
+ assert_equal 0, apple.position
13
+ assert_equal 1, orange.position
14
+ end
15
+
16
+ def test_it_ranks_on_insert_order
17
+ Fruit.create! name: 'apple'
18
+ Fruit.create! name: 'orange'
19
+
20
+ assert_equal ['apple', 'orange'], Fruit.ranked.map(&:name)
21
+ end
22
+ end
@@ -0,0 +1,70 @@
1
+ require_relative './support/test_helper'
2
+
3
+ class TestNamedRanking < Minitest::Test
4
+ def test_it_ranks_multiple_resources_together
5
+ apple = Fruit.create! name: 'apple'
6
+ carrot = Vegetable.create! name: 'carrot'
7
+
8
+ apple.rank :produce, 0
9
+ carrot.rank :produce, 1
10
+
11
+ assert_equal 0, apple.position
12
+ assert_equal 0, carrot.position
13
+
14
+ assert_equal 0, apple.position(:produce)
15
+ assert_equal 1, carrot.position(:produce)
16
+
17
+ assert_equal ['apple'], Fruit.ranked.map(&:name)
18
+ assert_equal ['carrot'], Vegetable.ranked.map(&:name)
19
+ end
20
+
21
+ def test_it_does_not_expand_scope
22
+ apple = Fruit.create! name: 'apple'
23
+ carrot = Vegetable.create! name: 'carrot'
24
+
25
+ apple.rank :produce, 0
26
+ carrot.rank :produce, 1
27
+
28
+ assert_equal 0, apple.position
29
+ assert_equal 0, carrot.position
30
+
31
+ assert_equal 0, apple.position(:produce)
32
+ assert_equal 1, carrot.position(:produce)
33
+
34
+ assert_equal ['apple'], Fruit.ranked(:produce).map(&:name)
35
+ assert_equal ['carrot'], Vegetable.ranked(:produce).map(&:name)
36
+ end
37
+
38
+ def test_it_can_access_the_global_scope
39
+ apple = Fruit.create! name: 'apple'
40
+ carrot = Vegetable.create! name: 'carrot'
41
+
42
+ apple.rank :produce, 0
43
+ carrot.rank :produce, 1
44
+
45
+ assert_equal 0, apple.position
46
+ assert_equal 0, carrot.position
47
+
48
+ assert_equal 0, apple.position(:produce)
49
+ assert_equal 1, carrot.position(:produce)
50
+
51
+ assert_equal ['apple', 'carrot'], Rankle.ranked(:produce).map(&:name)
52
+ end
53
+
54
+ def test_it_will_initialize_named_rankings_across_multiple_resources
55
+ Fruit.send :ranks, :produce
56
+ Vegetable.send :ranks, :produce
57
+
58
+ apple = Fruit.create! name: 'apple'
59
+ carrot = Vegetable.create! name: 'carrot'
60
+
61
+ assert_equal 0, apple.position
62
+ assert_equal 0, carrot.position
63
+
64
+ assert_equal 0, apple.position(:produce)
65
+ assert_equal 1, carrot.position(:produce)
66
+
67
+ # FIXME: This unfortunate hack reaches into the internals of RankleIndex to reset the test state
68
+ RankleIndex.instance_variable_set(:@rankers, {})
69
+ end
70
+ end
@@ -0,0 +1,75 @@
1
+ require_relative './support/test_helper'
2
+
3
+ class TestNamedRanking < Minitest::Test
4
+ def test_it_sets_the_position_for_the_named_ranking
5
+ apple = Fruit.create! name: 'apple'
6
+ orange = Fruit.create! name: 'orange'
7
+
8
+ apple.rank :reverse, 1
9
+ orange.rank :reverse, 0
10
+
11
+ assert_equal 0, apple.position
12
+ assert_equal 1, orange.position
13
+
14
+ assert_equal 1, apple.position(:reverse)
15
+ assert_equal 0, orange.position(:reverse)
16
+
17
+ assert_equal ['apple', 'orange'], Fruit.ranked.map(&:name)
18
+ assert_equal ['orange', 'apple'], Fruit.ranked(:reverse).map(&:name)
19
+ end
20
+
21
+ def test_it_does_not_initialize_named_rankings
22
+ apple = Fruit.create! name: 'apple'
23
+ banana = Fruit.create! name: 'banana'
24
+ orange = Fruit.create! name: 'orange'
25
+
26
+ apple.rank :reverse, 2
27
+ banana.rank :reverse, 1
28
+ orange.rank :reverse, 0
29
+
30
+ assert_equal 0, apple.position
31
+ assert_equal 1, banana.position
32
+ assert_equal 2, orange.position
33
+
34
+ assert_equal 1, apple.position(:reverse)
35
+ assert_equal 2, banana.position(:reverse)
36
+ assert_equal 0, orange.position(:reverse)
37
+
38
+ assert_equal ['apple', 'banana', 'orange'], Fruit.ranked.map(&:name)
39
+ assert_equal ['orange', 'apple', 'banana'], Fruit.ranked(:reverse).map(&:name)
40
+ end
41
+
42
+ def test_it_initializes_registered_named_rankings
43
+ Fruit.send :ranks, :reverse
44
+
45
+ apple = Fruit.create! name: 'apple'
46
+ banana = Fruit.create! name: 'banana'
47
+ orange = Fruit.create! name: 'orange'
48
+
49
+ assert_equal 0, apple.position
50
+ assert_equal 1, banana.position
51
+ assert_equal 2, orange.position
52
+
53
+ assert_equal 0, apple.position(:reverse)
54
+ assert_equal 1, banana.position(:reverse)
55
+ assert_equal 2, orange.position(:reverse)
56
+
57
+ apple.rank :reverse, 2
58
+ banana.rank :reverse, 1
59
+ orange.rank :reverse, 0
60
+
61
+ assert_equal 0, apple.position
62
+ assert_equal 1, banana.position
63
+ assert_equal 2, orange.position
64
+
65
+ assert_equal 2, apple.position(:reverse)
66
+ assert_equal 1, banana.position(:reverse)
67
+ assert_equal 0, orange.position(:reverse)
68
+
69
+ assert_equal ['apple', 'banana', 'orange'], Fruit.ranked.map(&:name)
70
+ assert_equal ['orange', 'banana', 'apple'], Fruit.ranked(:reverse).map(&:name)
71
+
72
+ # FIXME: This unfortunate hack reaches into the internals of RankleIndex to reset the test state
73
+ RankleIndex.instance_variable_set(:@rankers, {})
74
+ end
75
+ end
@@ -0,0 +1,14 @@
1
+ require_relative './support/test_helper'
2
+
3
+ class TestScopedRanking < Minitest::Test
4
+ def test_it_ranks_relative_to_a_scope
5
+ Fruit.class_exec { scope :berries, -> { where "name LIKE ?", '%berry' } }
6
+
7
+ ['apple', 'apricot', 'banana', 'bilberry', 'blackberry', 'blackcurrant', 'blueberry', 'boysenberry', 'cantaloupe'].each do |name|
8
+ Fruit.create! name: name
9
+ end
10
+
11
+ assert_equal ['apple', 'apricot', 'banana', 'bilberry', 'blackberry', 'blackcurrant', 'blueberry', 'boysenberry', 'cantaloupe'], Fruit.ranked.map(&:name)
12
+ assert_equal ['bilberry', 'blackberry', 'blueberry', 'boysenberry'], Fruit.berries.ranked.map(&:name)
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ require_relative './support/test_helper'
2
+
3
+ class TestSimpleUsage < Minitest::Test
4
+ def test_it_assigns_an_explicit_ranking
5
+ apple = Fruit.create! name: 'apple'
6
+ orange = Fruit.create! name: 'orange'
7
+
8
+ apple.update_attribute :position, 1
9
+
10
+ assert_equal 1, apple.position
11
+ assert_equal 0, orange.position
12
+
13
+ assert_equal ['orange', 'apple'], Fruit.ranked.map(&:name)
14
+ end
15
+
16
+ def test_it_sets_position_with_the_rank_method
17
+ apple = Fruit.create! name: 'apple'
18
+ orange = Fruit.create! name: 'orange'
19
+
20
+ apple.rank 1
21
+
22
+ assert_equal 1, apple.position
23
+ assert_equal 0, orange.position
24
+
25
+ assert_equal ['orange', 'apple'], Fruit.ranked.map(&:name)
26
+ end
27
+
28
+ def test_it_maintains_rank_with_a_proc
29
+ Fruit.send :ranks, ->(a, b) { a.name < b.name }
30
+
31
+ Fruit.create! name: 'apple'
32
+ Fruit.create! name: 'orange'
33
+ Fruit.create! name: 'banana'
34
+
35
+ assert_equal ['apple', 'banana', 'orange'], Fruit.ranked.map(&:name)
36
+
37
+ # FIXME: This unfortunate hack reaches into the internals of RankleIndex to reset the test state
38
+ RankleIndex.instance_variable_set(:@rankers, {})
39
+ end
40
+ end
@@ -0,0 +1,9 @@
1
+ #require_relative '../../lib/rankle_index'
2
+
3
+ class Fruit < ActiveRecord::Base
4
+ has_many :rankle_indices, as: :indexable
5
+ end
6
+
7
+ class Vegetable < ActiveRecord::Base
8
+ has_many :rankle_indices, as: :indexable
9
+ end
@@ -0,0 +1,15 @@
1
+ ActiveRecord::Schema.define do
2
+ self.verbose = false
3
+
4
+ create_table :fruits, :force => true do |t|
5
+ t.string :name
6
+
7
+ t.timestamps null: false
8
+ end
9
+
10
+ create_table :vegetables, :force => true do |t|
11
+ t.string :name
12
+
13
+ t.timestamps null: false
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+ require 'rankle'
4
+ require 'database_cleaner'
5
+ require 'rails/generators'
6
+ require 'rake'
7
+
8
+ ActiveRecord::Base.establish_connection(
9
+ adapter: 'sqlite3',
10
+ database: 'rankle.sqlite3'
11
+ )
12
+
13
+ rake = Rake.application
14
+ rake.init
15
+ rake.load_rakefile
16
+ Dir.entries(File.dirname(__FILE__) + '/../../db/migrate').each do |filename|
17
+ File.delete(File.dirname(__FILE__) + '/../../db/migrate/' + filename) rescue nil
18
+ end
19
+ Rails::Generators.invoke 'rankle:install'
20
+ require File.dirname(__FILE__) + '/../../db/migrate/' + Dir.entries(File.dirname(__FILE__) + '/../../db/migrate')[0]
21
+ File.delete 'rankle.sqlite3'
22
+ CreateRankleIndices.new.migrate :up
23
+
24
+ load File.dirname(__FILE__) + '/schema.rb'
25
+ load File.dirname(__FILE__) + '/models.rb'
26
+
27
+ DatabaseCleaner.strategy = :truncation
28
+
29
+ class Minitest::Test
30
+ def setup
31
+ DatabaseCleaner.start
32
+ end
33
+
34
+ def teardown
35
+ DatabaseCleaner.clean
36
+ end
37
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rankle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0.pre
4
+ version: 0.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wil
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-26 00:00:00.000000000 Z
11
+ date: 2015-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '10.0'
55
- - !ruby/object:Gem::Dependency
56
- name: cucumber
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: sqlite3
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -149,26 +135,21 @@ files:
149
135
  - LICENSE.txt
150
136
  - README.md
151
137
  - Rakefile
152
- - features/default_ranking.feature
153
- - features/get_position.feature
154
- - features/multi-resource_ranking.feature
155
- - features/named_ranking.feature
156
- - features/ranking.feature
157
- - features/set_position.feature
158
- - features/step_definitions/fruit_steps.rb
159
- - features/step_definitions/model_steps.rb
160
- - features/step_definitions/position_steps.rb
161
- - features/support/env.rb
162
- - features/support/factories/fruit.rb
163
- - features/support/factories/vegetable.rb
164
- - features/support/models.rb
165
- - features/support/schema.rb
166
138
  - lib/generators/rankle/install_generator.rb
167
139
  - lib/generators/rankle/templates/migration.rb
168
140
  - lib/rankle.rb
169
141
  - lib/rankle/ranker.rb
170
142
  - lib/rankle/version.rb
143
+ - lib/rankle_index.rb
171
144
  - rankle.gemspec
145
+ - test/default_behavior_test.rb
146
+ - test/multiple_resources_test.rb
147
+ - test/named_ranking_test.rb
148
+ - test/scoped_ranking_test.rb
149
+ - test/simple_usage_test.rb
150
+ - test/support/models.rb
151
+ - test/support/schema.rb
152
+ - test/support/test_helper.rb
172
153
  homepage: ''
173
154
  licenses:
174
155
  - MIT
@@ -184,9 +165,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
184
165
  version: '0'
185
166
  required_rubygems_version: !ruby/object:Gem::Requirement
186
167
  requirements:
187
- - - ">"
168
+ - - ">="
188
169
  - !ruby/object:Gem::Version
189
- version: 1.3.1
170
+ version: '0'
190
171
  requirements: []
191
172
  rubyforge_project:
192
173
  rubygems_version: 2.2.2
@@ -194,18 +175,12 @@ signing_key:
194
175
  specification_version: 4
195
176
  summary: Rankle provides multi-resource ranking.
196
177
  test_files:
197
- - features/default_ranking.feature
198
- - features/get_position.feature
199
- - features/multi-resource_ranking.feature
200
- - features/named_ranking.feature
201
- - features/ranking.feature
202
- - features/set_position.feature
203
- - features/step_definitions/fruit_steps.rb
204
- - features/step_definitions/model_steps.rb
205
- - features/step_definitions/position_steps.rb
206
- - features/support/env.rb
207
- - features/support/factories/fruit.rb
208
- - features/support/factories/vegetable.rb
209
- - features/support/models.rb
210
- - features/support/schema.rb
178
+ - test/default_behavior_test.rb
179
+ - test/multiple_resources_test.rb
180
+ - test/named_ranking_test.rb
181
+ - test/scoped_ranking_test.rb
182
+ - test/simple_usage_test.rb
183
+ - test/support/models.rb
184
+ - test/support/schema.rb
185
+ - test/support/test_helper.rb
211
186
  has_rdoc:
@@ -1,20 +0,0 @@
1
- Feature: Default ranking
2
- In order to not surprise new customers
3
- As a consumer of rankle
4
- I want the default behavior to match existing behavior
5
-
6
- Scenario: Empty fruit model
7
- Given an empty fruit model
8
- Then ranking all has no effect
9
- And the ranked fruit array is []
10
-
11
- Scenario: Fruit model with several fruits
12
- Given several fruits
13
- Then ranking all has no effect
14
- And the ranked fruit array is [Apple, Apricot, Banana, Bilberry, Blackberry, Blackcurrant, Blueberry, Boysenberry, Cantaloupe, Currant]
15
-
16
- Scenario: Ranked fruit
17
- Given an empty fruit model
18
- And an apple
19
- And an orange
20
- Then the ranked fruit array is [apple, orange]
@@ -1,19 +0,0 @@
1
- Feature: Get position
2
- In order to inspect a ranking
3
- As a developer
4
- I want to retrieve an element's position
5
-
6
- Scenario: Default ranking
7
- Given an apple
8
- And an orange
9
- Then the apple is in position 0
10
- And the orange is in position 1
11
- And the ranked fruit array is [apple, orange]
12
-
13
- Scenario: Custom ranking
14
- Given an apple
15
- And an orange
16
- When I assign the apple's position to 1
17
- Then the apple is in position 1
18
- And the orange is in position 0
19
- And the ranked fruit array is [orange, apple]
@@ -1,37 +0,0 @@
1
- Feature: Multi-resource ranking
2
- In order to rank multiple resources
3
- As a consumer of rankle
4
- I want to create a multi-resource ranking
5
-
6
- Scenario: Basic ranking
7
- Given a 'fruit' model
8
- And a 'vegetable' model
9
- And an 'apple' fruit
10
- And a 'carrot' vegetable
11
- When I assign the 'apple' fruit's 'produce' rank to '0'
12
- And I assign the 'carrot' vegetable's 'produce' rank to '1'
13
- Then the 'apple' fruit's 'default' rank is '0'
14
- And the 'carrot' vegetable's 'default' rank is '0'
15
- And the 'apple' fruit's 'produce' rank is '0'
16
- And the 'carrot' vegetable's 'produce' rank is '1'
17
- And the default ranked fruit array is [apple]
18
- And the default ranked vegetable array is [carrot]
19
- And the produce ranked fruit array is [apple]
20
- And the produce ranked vegetable array is [carrot]
21
- And the produce ranked rankle array is [apple, carrot]
22
-
23
-
24
- Scenario: Default ranking
25
- Given a 'fruit' model with a 'produce' ranking
26
- And a 'vegetable' model with a 'produce' ranking
27
- And an 'apple' fruit
28
- And a 'carrot' vegetable
29
- Then the 'apple' fruit's 'default' rank is '0'
30
- And the 'carrot' vegetable's 'default' rank is '0'
31
- And the 'apple' fruit's 'produce' rank is '0'
32
- And the 'carrot' vegetable's 'produce' rank is '1'
33
- And the default ranked fruit array is [apple]
34
- And the default ranked vegetable array is [carrot]
35
- And the produce ranked fruit array is [apple]
36
- And the produce ranked vegetable array is [carrot]
37
- And the produce ranked rankle array is [apple, carrot]
@@ -1,49 +0,0 @@
1
- Feature: Named ranking
2
- In order to maintain multiple rankings on a single class
3
- As a developer
4
- I want to create named rankings
5
-
6
- Scenario: Reverse ranking
7
- Given an apple
8
- And an orange
9
- When I assign the 'apple' fruit's 'reverse' rank to '1'
10
- When I assign the 'orange' fruit's 'reverse' rank to '0'
11
- Then the 'apple' fruit's 'default' rank is '0'
12
- And the 'orange' fruit's 'default' rank is '1'
13
- And the 'apple' fruit's 'reverse' rank is '1'
14
- And the 'orange' fruit's 'reverse' rank is '0'
15
- And the default ranked fruit array is [apple, orange]
16
- And the reverse ranked fruit array is [orange, apple]
17
-
18
- Scenario: Growing ranking
19
- Given an apple
20
- And a banana
21
- And an orange
22
- When I assign the 'apple' fruit's 'reverse' rank to '2'
23
- And I assign the 'banana' fruit's 'reverse' rank to '1'
24
- And I assign the 'orange' fruit's 'reverse' rank to '0'
25
- Then the 'apple' fruit's 'default' rank is '0'
26
- And the 'banana' fruit's 'default' rank is '1'
27
- And the 'orange' fruit's 'default' rank is '2'
28
- And the 'apple' fruit's 'reverse' rank is '1'
29
- And the 'banana' fruit's 'reverse' rank is '2'
30
- And the 'orange' fruit's 'reverse' rank is '0'
31
- And the default ranked fruit array is [apple, banana, orange]
32
- And the reverse ranked fruit array is [orange, apple, banana]
33
-
34
- Scenario: Registered ranking
35
- Given a 'fruit' class with a 'reverse' ranking
36
- And an apple
37
- And a banana
38
- And an orange
39
- When I assign the 'apple' fruit's 'reverse' rank to '2'
40
- And I assign the 'banana' fruit's 'reverse' rank to '1'
41
- And I assign the 'orange' fruit's 'reverse' rank to '0'
42
- Then the 'apple' fruit's 'default' rank is '0'
43
- And the 'banana' fruit's 'default' rank is '1'
44
- And the 'orange' fruit's 'default' rank is '2'
45
- And the 'apple' fruit's 'reverse' rank is '2'
46
- And the 'banana' fruit's 'reverse' rank is '1'
47
- And the 'orange' fruit's 'reverse' rank is '0'
48
- And the default ranked fruit array is [apple, banana, orange]
49
- And the reverse ranked fruit array is [orange, banana, apple]
@@ -1,24 +0,0 @@
1
- Feature: Ranks
2
- In order to provide an explicit ordering
3
- As a developer
4
- I want to rank on position
5
-
6
- Scenario: Reverse ranking
7
- Given 10 rows
8
- When I rank them in reverse order
9
- Then ranking is equivalent to all reversed
10
-
11
- Scenario: Update ranking
12
- Given 10 rows in default order
13
- When I move row 9 to row 0
14
- Then ranking is equivalent to all rotated -1
15
-
16
- Scenario: Negative rank
17
- Given 10 rows in default order
18
- When I move row 9 to row -10
19
- Then row 9 is in position 0
20
-
21
- Scenario: Out-of-bounds rank
22
- Given 10 rows in default order
23
- When I move row 0 to row 30
24
- Then row 0 is in position 9
@@ -1,41 +0,0 @@
1
- Feature: Set position
2
- In order to assign a ranking
3
- As a developer
4
- I want to set an element's position
5
-
6
- Scenario: Update position attribute
7
- Given an empty fruit model
8
- And an 'apple' fruit
9
- And an 'orange' fruit
10
- When I update the apple's position attribute to 1
11
- Then the apple is in position 1
12
- And the orange is in position 0
13
- Then the ranked fruit array is [orange, apple]
14
-
15
- Scenario: Update position attribute with rank method
16
- Given an apple
17
- And an orange
18
- When I assign the apple's rank to 1
19
- Then the apple is in position 1
20
- And the orange is in position 0
21
- Then the ranked fruit array is [orange, apple]
22
-
23
-
24
- Scenario: Override default with stabby proc
25
- Given a fruit class with a reverse alphabetical default ranking on name
26
- And an apple
27
- And an orange
28
- Then the apple is in position 1
29
- And the orange is in position 0
30
- Then the ranked fruit array is [orange, apple]
31
-
32
-
33
- Scenario: Override default with stabby proc (documentation)
34
- Given a fruit class with an alphabetical default ranking on name
35
- And an apple
36
- And an orange
37
- And a banana
38
- Then the apple is in position 0
39
- And the banana is in position 1
40
- And the orange is in position 2
41
- Then the ranked fruit array is [apple, banana, orange]
@@ -1,29 +0,0 @@
1
- Given(/^an apple$/) do
2
- DatabaseCleaner.clean
3
- @fruit ||= {}
4
- @fruit[:apple] = create :fruit, name: 'apple'
5
- end
6
-
7
- Given(/^an orange$/) do
8
- @fruit[:orange] = create :fruit, name: 'orange'
9
- end
10
-
11
- Given(/^a banana$/) do
12
- @fruit[:banana] = create :fruit, name: 'banana'
13
- end
14
-
15
- Then(/^the apple is in position (\d+)$/) do |position|
16
- expect(@fruit[:apple].position).to eq(position.to_i)
17
- end
18
-
19
- Then(/^the orange is in position (\d+)$/) do |position|
20
- expect(@fruit[:orange].position).to eq(position.to_i)
21
- end
22
-
23
- Then(/^the banana is in position (\d+)$/) do |position|
24
- expect(@fruit[:banana].position).to eq(position.to_i)
25
- end
26
-
27
- When(/^I assign the apple's position to (\d+)$/) do |position|
28
- @fruit[:apple].update_attribute(:position, position.to_i)
29
- end
@@ -1,101 +0,0 @@
1
- Given(/^a '(.*)' model$/) do |name|
2
- DatabaseCleaner.clean
3
- name.classify.constantize.delete_all
4
- end
5
-
6
- Given(/^a '(.*)' model with a '(.*)' ranking$/) do |klass, ranking|
7
- step "a '#{klass}' model"
8
- klass.classify.constantize.send :ranks, ranking.to_sym
9
- end
10
-
11
-
12
- Given(/^an '(.*)' fruit$/) do |name|
13
- @fruit ||= {}
14
- @fruit[name.to_sym] = create :fruit, name: name
15
- end
16
-
17
- Given(/^a '(.*)' vegetable$/) do |name|
18
- @vegetable ||= {}
19
- @vegetable[name.to_sym] = create :vegetable, name: name
20
- end
21
-
22
- Given(/^an empty (.+) model$/) do |klass|
23
- DatabaseCleaner.clean
24
- klass.classify.constantize.delete_all
25
- end
26
-
27
- Given(/^several fruits$/) do
28
- DatabaseCleaner.clean
29
- @fruit ||= {}
30
- 10.times.each do |index|
31
- fruit = create :fruit
32
- @fruit[fruit.name.to_sym] = fruit
33
- end
34
- end
35
-
36
- Given(/^several points$/) do
37
- DatabaseCleaner.clean
38
- 10.times.each { |index| Point.create!(x: index, y: index) }
39
- end
40
-
41
- Given(/^(\d+) rows$/) do |count|
42
- DatabaseCleaner.clean
43
- count.to_i.times.each { |index| Row.create!(text: index) }
44
- end
45
-
46
- Given(/^(\d+) rows in default order$/) do |count|
47
- DatabaseCleaner.clean
48
- count.to_i.times.each { |index| Row.create!(text: index).update_attribute(:position, index) }
49
- end
50
-
51
- Given(/^(\d+) even and odd rows$/) do |count|
52
- DatabaseCleaner.clean
53
- count.to_i.times.each { |index| Row.create!(text: index.even? ? 'even' : 'odd').update_attribute(:position, index) }
54
- end
55
-
56
- When(/^I rank them in reverse order$/) do
57
- Row.all.reverse.each_with_index { |row, index| row.update_attribute(:position, index) }
58
- end
59
-
60
- When(/^I move row (\d+) to row (-?\d+)$/) do |start_position, end_position|
61
- Row.all[start_position.to_i].update_attribute(:position, end_position.to_i)
62
- end
63
-
64
- When(/^I reverse rank the even rows$/) do
65
- Row.rank(:even)
66
- end
67
-
68
- Then(/^ranking is equivalent to all reversed$/) do
69
- expect(Row.ranked.to_a).to eq(Row.all.to_a.reverse)
70
- end
71
-
72
- Then(/^ranking is equivalent to all rotated (\-\d+)$/) do |positions|
73
- expect(Row.ranked.to_a).to eq(Row.all.to_a.rotate(positions.to_i))
74
- end
75
-
76
- Then(/^ranking all has no effect$/) do
77
- expect(Point.ranked.to_a).to eq(Point.all.to_a)
78
- end
79
-
80
- Then(/^row (\d+) is in position (\d+)$/) do |row, position|
81
- expect(Row.ranked[position.to_i].id).to eq(row.to_i + 1)
82
- end
83
-
84
- Given(/^a fruit class with an alphabetical default ranking on name$/) do
85
- Fruit.send :ranks, ->(a, b) { a.name < b.name }
86
- end
87
-
88
- Given(/^a fruit class with a reverse alphabetical default ranking on name$/) do
89
- Fruit.send :ranks, ->(a, b) { a.name > b.name }
90
- end
91
-
92
- Given(/^a 'fruit' class with a 'reverse' ranking$/) do
93
- Fruit.send :ranks, :reverse
94
- end
95
-
96
- Then(/^the (.*)ranked (.*) array is \[(.*)\]$/) do |ranker, klass, names|
97
- ranker = 'default' if ranker.blank?
98
- expect(klass.classify.constantize.ranked(ranker.strip.to_sym).map(&:name)).to eq(names.split(',').map do |name|
99
- @fruit[name.strip.to_sym].name rescue @vegetable[name.strip.to_sym].name
100
- end)
101
- end
@@ -1,24 +0,0 @@
1
- When(/^I update the apple's position attribute to (\d+)$/) do |position|
2
- @fruit[:apple].update_attribute(:position, position.to_i)
3
- end
4
-
5
- When(/^I assign the apple's rank to (\d+)$/) do |position|
6
- @fruit[:apple].rank position.to_i
7
- end
8
-
9
- When(/^I assign the '(.*)' fruit's '(.*)' rank to '(\d+)'$/) do |type, name, position|
10
- @fruit[type.to_sym].rank name.to_sym, position.to_i
11
- end
12
-
13
- When(/^I assign the '(.*)' vegetable's '(.*)' rank to '(\d+)'$/) do |type, name, position|
14
- @vegetable[type.to_sym].rank name.to_sym, position.to_i
15
- end
16
-
17
-
18
- Then(/^the '(.*)' fruit's '(.*)' rank is '(\d+)'$/) do |type, name, position|
19
- expect(@fruit[type.to_sym].position name).to eq(position.to_i)
20
- end
21
-
22
- Then(/^the '(.*)' vegetable's '(.*)' rank is '(\d+)'$/) do |type, name, position|
23
- expect(@vegetable[type.to_sym].position name).to eq(position.to_i)
24
- end
@@ -1,15 +0,0 @@
1
- require 'rankle'
2
- require 'database_cleaner'
3
- require 'factory_girl'
4
-
5
- DatabaseCleaner.strategy = :truncation
6
-
7
- ActiveRecord::Base.establish_connection(
8
- adapter: 'sqlite3',
9
- database: 'rankle.sqlite3'
10
- )
11
-
12
- load File.dirname(__FILE__) + '/schema.rb'
13
- load File.dirname(__FILE__) + '/models.rb'
14
-
15
- World FactoryGirl::Syntax::Methods
@@ -1,16 +0,0 @@
1
- FactoryGirl.define do
2
- factory :fruit do
3
- sequence(:name) { |n| FRUITS[n-1] }
4
- end
5
- end
6
-
7
- FRUITS = ['Apple', 'Apricot', 'Banana', 'Bilberry', 'Blackberry', 'Blackcurrant', 'Blueberry', 'Boysenberry',
8
- 'Cantaloupe', 'Currant', 'Cherry', 'Cherimoya', 'Cloudberry', 'Coconut', 'Cranberry', 'Damson', 'Date',
9
- 'Dragonfruit', 'Durian', 'Elderberry', 'Feijoa', 'Fig', 'Goji berry', 'Gooseberry', 'Grape', 'Raisin',
10
- 'Grapefruit', 'Guava', 'Huckleberry', 'Jackfruit', 'Jambul', 'Jujube', 'Kiwi fruit', 'Kumquat', 'Lemon',
11
- 'Lime', 'Loquat', 'Lychee', 'Mango', 'Marion berry', 'Melon', 'Cantaloupe', 'Honeydew', 'Watermelon',
12
- 'Rock melon', 'Miracle fruit', 'Mulberry', 'Nectarine', 'Olive', 'Orange', 'Clementine', 'Mandarine',
13
- 'Tangerine', 'Papaya', 'Passionfruit', 'Peach', 'Pear', 'Williams pear', 'Bartlett pear', 'Persimmon',
14
- 'Physalis', 'Plum/prune (dried plum)', 'Pineapple', 'Pomegranate', 'Pomelo', 'Purple Mangosteen', 'Quince',
15
- 'Raspberry', 'Salmon berry', 'Black raspberry', 'Rambutan', 'Redcurrant', 'Salal berry', 'Satsuma',
16
- 'Star fruit', 'Strawberry', 'Tamarillo', 'Ugli fruit']
@@ -1,36 +0,0 @@
1
- FactoryGirl.define do
2
- factory :vegetable do
3
- sequence(:name) { |n| VEGETABLES[n-1] }
4
- end
5
-
6
- #factory :legume, class: Vegetable do
7
- # sequence(:name) { |n| LEGUMES[n-1] }
8
- #end
9
- end
10
-
11
- VEGETABLES = ['Artichoke', 'Arugula', 'Asparagus', 'Amaranth', 'Bok choy', 'Broccoflower', 'Broccoli',
12
- 'Brussels sprouts', 'Cabbage', 'Calabrese', 'Cannabis', 'Carrots', 'Cauliflower', 'Celery', 'Chard',
13
- 'Collard greens', 'Corn salad', 'Eggplant', 'Endive', 'Fiddleheads', 'Frisee', 'Kale', 'Kohlrabi',
14
- 'Lettuce Lactuca sativa', 'Corn', 'Mushrooms', 'Mustard greens', 'Nettles', 'New Zealand spinach', 'Okra',
15
- 'Parsley', 'Radicchio', 'Rhubarb', 'Salsify', 'Skirret', 'Spinach', 'Topinambur', 'Tat soi', 'Tomato',
16
- 'Water chestnut', 'Watercress']
17
-
18
- LEGUMES = ['Alfalfa sprouts', 'Azuki beans', 'Bean sprouts', 'Black beans', 'Black-eyed peas', 'Borlotti bean',
19
- 'Broad beans', 'Chickpeas', 'Green beans', 'Kidney beans', 'Lentils', 'Lima beans', 'Mung beans',
20
- 'Navy beans', 'Pinto beans', 'Runner beans', 'Soy beans', 'Snap peas']
21
-
22
- HERBS_AND_SPICES = ['Anise', 'Basil', 'Caraway', 'Cilantro', 'Coriander', 'Chamomile', 'Dill', 'Fennel', 'Lavender',
23
- 'Lemon Grass', 'Marjoram', 'Oregano', 'Parsley', 'Rosemary', 'Sage', 'Thyme']
24
-
25
- ONIONS = ['Chives', 'Garlic', 'Leek Allium porrum', 'Onion', 'Shallot', 'Scallion']
26
-
27
- PEPPERS = ['Bell pepper', 'Chili pepper', 'Jalapeno', 'Habanero', 'Paprika', 'Tabasco pepper', 'Cayenne pepper']
28
-
29
- ROOT_VEGETABLES = ['Beet', 'Carrot', 'Celeriac', 'Daikon', 'Ginger', 'Parsnip', 'Rutabaga', 'Turnip']
30
-
31
- RADISH = ['Rutabaga', 'Turnip', 'Wasabi', 'Horseradish', 'White radish']
32
-
33
- SQUASHES = ['Acorn squash', 'Butternut squash', 'Banana squash', 'Zucchini', 'Cucumber', 'Delicata', 'Gem squash',
34
- 'Hubbard squash', 'Squash', 'Patty pans', 'Pumpkin', 'Spaghetti squash']
35
-
36
- TUBERS = ['Jicama', 'Jerusalem artichoke', 'Potato', 'Sweet potato', 'Taro', 'Yam']
@@ -1,19 +0,0 @@
1
- class RankleIndex < ActiveRecord::Base
2
- belongs_to :indexable, polymorphic: true
3
- end
4
-
5
- class Fruit < ActiveRecord::Base
6
- has_many :rankle_indices, as: :indexable
7
- end
8
-
9
- class Vegetable < ActiveRecord::Base
10
- has_many :rankle_indices, as: :indexable
11
- end
12
-
13
- class Point < ActiveRecord::Base
14
- has_many :rankle_indices, as: :indexable
15
- end
16
-
17
- class Row < ActiveRecord::Base
18
- has_many :rankle_indices, as: :indexable
19
- end
@@ -1,37 +0,0 @@
1
- ActiveRecord::Schema.define do
2
- self.verbose = false
3
-
4
- create_table :fruits, :force => true do |t|
5
- t.string :name
6
-
7
- t.timestamps null: false
8
- end
9
-
10
- create_table :vegetables, :force => true do |t|
11
- t.string :name
12
-
13
- t.timestamps null: false
14
- end
15
-
16
- create_table :points, :force => true do |t|
17
- t.integer :x
18
- t.integer :y
19
-
20
- t.timestamps null: false
21
- end
22
-
23
- create_table :rows, :force => true do |t|
24
- t.string :text
25
-
26
- t.timestamps null: false
27
- end
28
-
29
- create_table :rankle_indices, :force => true do |t|
30
- t.string :indexable_name
31
- t.integer :indexable_id
32
- t.string :indexable_type
33
- t.integer :indexable_position
34
-
35
- t.timestamps null: false
36
- end
37
- end