Module: VoteFu::Concerns::Karmatic::InstanceMethods

Defined in:
lib/vote_fu/concerns/karmatic.rb

Instance Method Summary collapse

Instance Method Details

#karma(force: false) ⇒ Integer

Calculate total karma from all sources

Parameters:

  • force (Boolean) (defaults to: false)

    Bypass cache

Returns:

  • (Integer)

    Total karma points



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/vote_fu/concerns/karmatic.rb', line 119

def karma(force: false)
  return 0 unless self.class.respond_to?(:karma_sources)

  # Use cached value if available and not forcing recalculation
  if !force && has_attribute?(:karma_cache) && karma_cache.present?
    return karma_cache
  end

  calculated = self.class.karma_sources.sum do |source|
    calculate_karma_for(source)
  end.round

  # Update cache if column exists
  update_karma_cache(calculated) if has_attribute?(:karma_cache) && force

  calculated
end

#karma_breakdownArray<Hash>

Get karma breakdown by source

Returns:

  • (Array<Hash>)

    Array of value:, recent: hashes



152
153
154
155
156
157
158
159
160
161
162
# File 'lib/vote_fu/concerns/karmatic.rb', line 152

def karma_breakdown
  return [] unless self.class.respond_to?(:karma_sources)

  self.class.karma_sources.map do |source|
    {
      source: source[:association],
      value: calculate_karma_for(source).round,
      recent: calculate_karma_for(source, since: 30.days.ago).round
    }
  end
end

#karma_for(association) ⇒ Integer

Get karma for a specific source

Parameters:

  • association (Symbol)

    The association name

Returns:

  • (Integer)


168
169
170
171
172
173
# File 'lib/vote_fu/concerns/karmatic.rb', line 168

def karma_for(association)
  source = self.class.karma_sources.find { |s| s[:association] == association }
  return 0 unless source

  calculate_karma_for(source).round
end

#karma_levelString

Get the user's karma level

Returns:

  • (String)

    Level name



178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/vote_fu/concerns/karmatic.rb', line 178

def karma_level
  return "Unknown" unless self.class.respond_to?(:karma_levels)

  current_karma = karma
  level = "Unknown"

  self.class.karma_levels.sort_by { |k, _| k }.each do |threshold, name|
    level = name if current_karma >= threshold
  end

  level
end

#karma_level?(level_name) ⇒ Boolean

Check if user has at least a certain karma level

Parameters:

  • level_name (String)

    The level name to check

Returns:

  • (Boolean)


245
246
247
248
249
250
251
252
# File 'lib/vote_fu/concerns/karmatic.rb', line 245

def karma_level?(level_name)
  return false unless self.class.respond_to?(:karma_levels)

  threshold = self.class.karma_levels.key(level_name)
  return false unless threshold

  karma >= threshold
end

#karma_progressHash

Get progress to next karma level

Returns:

  • (Hash)

    { current_level:, next_level:, progress:, karma_needed: }



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/vote_fu/concerns/karmatic.rb', line 194

def karma_progress
  return nil unless self.class.respond_to?(:karma_levels)

  current_karma = karma
  sorted_levels = self.class.karma_levels.sort_by { |k, _| k }

  current_threshold = 0
  current_level = sorted_levels.first&.last || "Unknown"
  next_threshold = nil
  next_level = nil

  sorted_levels.each_with_index do |(threshold, name), i|
    if current_karma >= threshold
      current_threshold = threshold
      current_level = name
      # Only set next level if there IS a next level
      if i < sorted_levels.length - 1
        next_data = sorted_levels[i + 1]
        next_threshold = next_data[0]
        next_level = next_data[1]
      else
        next_threshold = nil
        next_level = nil
      end
    end
  end

  if next_threshold.nil?
    # Already at max level
    {
      current_level: current_level,
      next_level: nil,
      progress: 100.0,
      karma_needed: 0
    }
  else
    range = next_threshold - current_threshold
    progress_in_range = current_karma - current_threshold
    {
      current_level: current_level,
      next_level: next_level,
      progress: ((progress_in_range.to_f / range) * 100).round(1),
      karma_needed: next_threshold - current_karma
    }
  end
end

#recalculate_karma!Object

Alias for update_karma_cache



263
264
265
266
# File 'lib/vote_fu/concerns/karmatic.rb', line 263

def recalculate_karma!
  update_karma_cache
  karma(force: true)
end

#recent_karma(days: 30) ⇒ Integer

Get karma for last N days only

Parameters:

  • days (Integer) (defaults to: 30)

    Number of days to look back

Returns:

  • (Integer)


141
142
143
144
145
146
147
# File 'lib/vote_fu/concerns/karmatic.rb', line 141

def recent_karma(days: 30)
  return 0 unless self.class.respond_to?(:karma_sources)

  self.class.karma_sources.sum do |source|
    calculate_karma_for(source, since: days.days.ago)
  end.round
end

#update_karma_cache(value = nil) ⇒ Object

Update the karma cache (call periodically or after votes)



255
256
257
258
259
260
# File 'lib/vote_fu/concerns/karmatic.rb', line 255

def update_karma_cache(value = nil)
  return unless has_attribute?(:karma_cache)

  value ||= self.class.karma_sources.sum { |s| calculate_karma_for(s) }.round
  update_column(:karma_cache, value)
end