From e03eea710bba422046fb993d0f17ccba6d74629f Mon Sep 17 00:00:00 2001
From: Mauricio Giacomini Girardello <mauriciogiacomini4@gmail.com>
Date: Mon, 31 Aug 2015 16:23:14 -0300
Subject: [PATCH] refactoring ranking module decouple rater class from sort
 alg, create concrete classes for item, rated item and basic rater strategy

---
 lib/ranking/README.md                 | 29 +++++++--------
 lib/ranking/item.rb                   | 18 +++++++++
 lib/ranking/rank.rb                   | 53 ---------------------------
 lib/ranking/rated_item.rb             | 10 +++++
 lib/ranking/rater.rb                  | 11 ++++++
 lib/ranking/strategies/basic_rater.rb | 26 +++++++++++++
 lib/ranking/strategy.rb               | 15 ++++++++
 7 files changed, 94 insertions(+), 68 deletions(-)
 create mode 100644 lib/ranking/item.rb
 delete mode 100644 lib/ranking/rank.rb
 create mode 100644 lib/ranking/rated_item.rb
 create mode 100644 lib/ranking/rater.rb
 create mode 100644 lib/ranking/strategies/basic_rater.rb
 create mode 100644 lib/ranking/strategy.rb

diff --git a/lib/ranking/README.md b/lib/ranking/README.md
index a05757ba7..0f01c85eb 100644
--- a/lib/ranking/README.md
+++ b/lib/ranking/README.md
@@ -1,19 +1,18 @@
-# Use Example
-### Input :
-    items = [Item.new("ax",1,1,1,["otherStuffA","lala",'a']),Item.new("bx",2,2,2,[1,2,3,"teste","lblb",'b']),Item.new("cx",1,1,99,[3,2,1,"teste","lclc",'c'])]
+# Ranking docs
+ 
+# Refactored
 
-### Rank call :
+```ruby
+items = []
+items << Ranking::Item.new("ax",1,1,1,["otherStuffA","lala",'a'])
+items << Ranking::Item.new("bx",2,2,2,[1,2,3,"teste","lblb",'b'])
+items << Ranking::Item.new("cx",1,1,99,[3,2,1,"teste","lclc",'c'])
 
-    Rater.new.sortByRate(items).each { |i| puts i }
-    
-###### OR
+rater = Ranking::Rater.new(Ranking::Strategies::BasicRater.new(1000, 1, 100))
+rater.sortByRate(items).each do |i|
+    p i.name
+end
+```
 
-    Rater.new( # Order of the arguments is important!
-        positionWeight = 1000,
-        useWeight = 1, 
-        likeWeight = 100
-    ).sortByRate(items).each { |i| puts i }
-    
-    
-#### Return (output) : 
+## Output
  - ordered array of Items, starting from the most ranked to the least
\ No newline at end of file
diff --git a/lib/ranking/item.rb b/lib/ranking/item.rb
new file mode 100644
index 000000000..33fc5009b
--- /dev/null
+++ b/lib/ranking/item.rb
@@ -0,0 +1,18 @@
+# This class represents an item of ranking
+class Ranking::Item
+  # name = name of object
+  # views = view count
+  # downloads = download count
+  # likes = like count
+  # info = just for convinience of sorting it together, won't be touched
+  attr_accessor :name, :views, :downloads, :likes, :info
+
+  def initialize(name, views = 0, downloads = 0, likes = 0, info = nil)
+    @name = name
+    @views = views
+    @downloads = downloads
+    @likes = likes
+    @info = info
+  end
+
+end
\ No newline at end of file
diff --git a/lib/ranking/rank.rb b/lib/ranking/rank.rb
deleted file mode 100644
index 73698a258..000000000
--- a/lib/ranking/rank.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# Input: Array of struct Item (sorted by DB name matching)
-# Output: Array of struct Item sorted by ranking
-
-Item = Struct.new :name,:views,:downloads,:likes,:otherInfo # For interfacing
-# name = name of object
-# views = view count
-# downloads = download count
-# likes = like count
-# otherInfo = Anything, just for convinience of sorting it together, won't be touched
-
-class Rater #INTERFACE
-  def initialize positionWeight = nil, useWeight = nil, likeWeight = nil
-    @rater = BasicRater.new positionWeight, useWeight, likeWeight
-  end
-  def sortByRate items
-    @rater.sortByRate items
-  end
-end
-
-class BasicRater # Don't instanciate this directly, use Rater
-  
-  RatedItem = Struct.new(:item,:rate)
-  
-  def initialize positionWeight = nil, useWeight = nil, likeWeight = nil
-    @positionWeight = (positionWeight == nil ? 1000 : positionWeight)
-    @useWeight = (useWeight == nil ? 3 : useWeight)
-    @likeWeight = (likeWeight == nil ? 100 : likeWeight)
-  end
-
-  def rateItem item, inversePosition # Returns: RatedItem instance
-
-    positionRating = inversePosition * @positionWeight
-    useRating = (item.downloads < item.views ? item.downloads : item.views)*@useWeight
-    likeRating = item.likes * @likeWeight
-
-    rate = positionRating + useRating + likeRating
-    RatedItem.new( item, rate )
-
-  end
-
-  def sortByRate items
-    first = items.first
-    rest = items.drop(1)
-
-    rest.zip( rest.size.downto(1) )
-        .collect { |item,reverseIndex| self.rateItem( item, reverseIndex ) } # Returns RatedItem instance
-        .sort { |itemA,itemB| itemA.rate <=> itemB.rate }
-        .collect { |ri| ri.item }
-        .push( first )
-        .reverse # Best ranked comes first
-  end
-
-end
diff --git a/lib/ranking/rated_item.rb b/lib/ranking/rated_item.rb
new file mode 100644
index 000000000..baa5aa8ec
--- /dev/null
+++ b/lib/ranking/rated_item.rb
@@ -0,0 +1,10 @@
+# This class represents an item rated in ranking
+#  Have an association with a rank item
+class Ranking::RatedItem
+  attr_accessor :item, :rate
+
+  def initialize(item, rate)
+    @item = item
+    @rate = rate
+  end
+end
\ No newline at end of file
diff --git a/lib/ranking/rater.rb b/lib/ranking/rater.rb
new file mode 100644
index 000000000..75b3d4018
--- /dev/null
+++ b/lib/ranking/rater.rb
@@ -0,0 +1,11 @@
+class Ranking::Rater
+
+  def initialize(rater_strategy)
+    @rater = rater_strategy
+  end
+
+  def sortByRate(items)
+    @rater.sortByRate items
+  end
+
+end
\ No newline at end of file
diff --git a/lib/ranking/strategies/basic_rater.rb b/lib/ranking/strategies/basic_rater.rb
new file mode 100644
index 000000000..8c03c3d29
--- /dev/null
+++ b/lib/ranking/strategies/basic_rater.rb
@@ -0,0 +1,26 @@
+class Ranking::Strategies::BasicRater < Ranking::Strategy
+
+  def sortByRate(items)
+    first = items.first
+    rest = items.drop(1)
+
+    rest.zip(rest.size.downto(1))
+        .collect { |item, reverseIndex| rateItem(item, reverseIndex) } # Returns RatedItem instance
+        .sort { |itemA, itemB| itemA.rate <=> itemB.rate }
+        .collect { |ri| ri.item }
+        .push(first)
+        .reverse # Best ranked comes first
+  end
+
+  private
+
+  def rateItem(item, inversePosition)
+    positionRating = inversePosition * @positionWeight
+    useRating = (item.downloads < item.views ? item.downloads : item.views) * @useWeight
+    likeRating = item.likes * @likeWeight
+
+    rate = positionRating + useRating + likeRating
+    build_rated_item item, rate
+  end
+
+end
\ No newline at end of file
diff --git a/lib/ranking/strategy.rb b/lib/ranking/strategy.rb
new file mode 100644
index 000000000..ffce28174
--- /dev/null
+++ b/lib/ranking/strategy.rb
@@ -0,0 +1,15 @@
+class Ranking::Strategy
+
+  def initialize(positionWeight = nil, useWeight = nil, likeWeight = nil)
+    @positionWeight = (positionWeight == nil ? 1000 : positionWeight)
+    @useWeight = (useWeight == nil ? 3 : useWeight)
+    @likeWeight = (likeWeight == nil ? 100 : likeWeight)
+  end
+
+  protected
+
+  def build_rated_item(item, rate)
+    Ranking::RatedItem.new(item, rate)
+  end
+
+end
\ No newline at end of file
-- 
GitLab