Completed
Pull Request — master (#145)
by
unknown
01:56
created

SchedulesHelper.balanced_session_columns_for_slot()   D

Complexity

Conditions 10

Size

Total Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
dl 0
loc 53
rs 4.8
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like SchedulesHelper.balanced_session_columns_for_slot() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
module SchedulesHelper
2
3
  def pill_label(slot)
4
    slot.starts_at.in_time_zone.to_s(:usahhmm)
5
  end
6
7
  def session_columns_for_slot(slot, &block)
8
    if params[:stable_room_order].to_i == 1
9
      stable_room_order_session_columns_for_slot(slot, &block)
10
    else
11
      balanced_session_columns_for_slot(slot, &block)
12
    end
13
  end
14
15
  def stable_room_order_session_columns_for_slot(slot, &block)
16
    sessions = slot.sessions.sort_by { |s| session_sort_order(s) }
17
    split = (sessions.size+1) / 2
18
    yield sessions[0...split]
19
    yield sessions[split..-1]
20
  end
21
22
  ##
23
  # Attempt to divide these sessions into two roughly equal groups of roughly equal height.
24
  # (Without this, the fully expanded details grow very lopsided.)
25
  def balanced_session_columns_for_slot(slot, &block)
26
27
    unassigned = slot.sessions.sort_by { |s| -estimated_height(s) }
28
29
    columns = [[], []]
30
    heights = [0, 0]
31
    i = 0
32
    first = true
33
    until unassigned.empty?
34
      if unassigned.size == 1 && columns[0].size == columns[1].size  # odd number of sessions, so last one can go in either column
35
        i = if heights[0] < heights[1]
36
          0
37
        else
38
          1
39
        end
40
      end
41
42
      if first
43
        # Start by placing longest description
44
        session = unassigned.shift
45
        first = false
46
      else
47
        # Greedy algo: choose next session to try to keep heights as close as possible
48
        desired_height = heights[1-i] - heights[i]
49
        session = nil
50
        best_diff = 1 / 0.0
51
        unassigned.each do |candidate|  # O(n^2), so watch this one if we start assigning lots of sessions per slot!
52
          diff = (estimated_height(candidate) - desired_height).abs
53
          if diff < best_diff
54
            best_diff = diff
55
            session = candidate
56
          end
57
        end
58
        unassigned.delete(session)
59
      end
60
      break unless session
61
62
      columns[i] << session
63
      heights[i] += estimated_height(session)
64
      i = 1-i
65
    end
66
67
    # Now yield each column with session sorted by room size.
68
69
    columns.map! { |col| col.sort_by { |s| session_sort_order(s) } }
70
    unless columns[0].empty? || columns[1].empty?
71
      if session_room_capacity(columns[0].first) < session_room_capacity(columns[1].first)
72
        columns = [columns[1], columns[0]]
73
      end
74
    end
75
76
    columns.each(&block)
77
  end
78
79
private
80
81
  def session_room_capacity(session)
82
    session.room&.capacity || 0
0 ignored issues
show
Bug introduced by
The Ruby parser could not interpret the code. It reported: unexpected token error (Using Ruby 2.0 pa...meter, under `AllCops`).
Loading history...
83
  end
84
85
  def session_sort_order(session)
86
    [-session.attendance_count, session.room&.name || ""]
0 ignored issues
show
Bug introduced by
The Ruby parser could not interpret the code. It reported: unexpected token error (Using Ruby 2.0 pa...meter, under `AllCops`).
Loading history...
Bug introduced by
The Ruby parser could not interpret the code. It reported: unexpected token tRBRACK (Using Ruby 2.0 ...meter, under `AllCops`).
Loading history...
87
  end
88
89
  def estimated_height(session)
90
    if session.instance_variable_get(:@estimated_height).blank?
91
      h = 0
92
      h += (session.title.length / 42 + 1) * 25
93
      h += (session.presenters.size / 5 + 1) * 20
94
      h += session.description.length / 4 + 17
95
      session.presenters.each { |presenter| h += (presenter.bio || '').length / 5 + 30}
96
      session.instance_variable_set(:@estimated_height, h)
97
    end
98
    session.instance_variable_get(:@estimated_height)
99
  end
100
101
end
102
0 ignored issues
show
Bug introduced by
The Ruby parser could not interpret the code. It reported: unexpected token $end (Using Ruby 2.0 par...meter, under `AllCops`).
Loading history...