Module: IsReviewable::Reviewable::ClassMethods

Defined in:
lib/is_reviewable/reviewable.rb

Instance Method Summary collapse

Instance Method Details

#is_reviewable(*args) ⇒ Object

TODO: Document this method…thoroughly.

Examples:

is_reviewable :by => :user, :scale => 0..5, :total_precision => 2

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/is_reviewable/reviewable.rb', line 40

def is_reviewable(*args)
  options = args.extract_options!
  options.reverse_merge!(
      :by         => nil,
      :scale      => options[:values] || options[:range] || DEFAULTS[:scale],
      :accept_ip  => options[:anonymous] || DEFAULTS[:accept_ip] # i.e. also accepts unique IPs as reviewer
    )
  scale = options[:scale]
  if options[:step].blank? && options[:steps].blank?
    options[:steps] = scale.last - scale.first + 1
  else
    # use :step or :steps beneath
  end
  options[:total_precision] ||= options[:average_precision] || scale.first.to_s.split('.').last.size # == 1
  
  # Check for incorrect input values, and handle ranges of floats with help of :step. E.g. :scale => 1.0..5.0.
  
  if scale.is_a?(::Range) && scale.first.is_a?(::Float)
    options[:step] = (scale.last - scale.first) / (options[:steps] - 1) if options[:step].blank?
    options[:scale] = scale.first.step(scale.last, options[:step]).collect { |value| value }
  else
    options[:scale] = scale.to_a.collect! { |v| v.to_f }
  end
  raise InvalidConfigValueError, ":scale/:range/:values must consist of numeric values only." unless options[:scale].all? { |v| v.is_a?(::Numeric) }
  raise InvalidConfigValueError, ":total_precision must be an integer." unless options[:total_precision].is_a?(::Fixnum)
  
  # Assocations: Review class (e.g. Review).
  options[:review_class] = ASSOCIATION_CLASS
  
  # Had to do this here - not sure why. Subclassing Review should be enough? =S
  "::#{options[:review_class]}".constantize.class_eval do
    belongs_to :reviewable, :polymorphic => true unless self.respond_to?(:reviewable)
    belongs_to :reviewer,   :polymorphic => true unless self.respond_to?(:reviewer)
  end
  
  # Reviewer class(es).
  options[:reviewer_classes] = [*options[:by]].collect do |class_name|
    begin
      class_name.to_s.singularize.classify.constantize
    rescue NameError => e
      raise InvalidReviewerError, "Reviewer class #{class_name} not defined, needs to be defined. #{e}"
    end
  end
  
  # Assocations: Reviewer class(es) (e.g. User, Account, ...).
  options[:reviewer_classes].each do |reviewer_class|
    if ::Object.const_defined?(reviewer_class.name.to_sym)
      reviewer_class.class_eval do
        #has_many  :reviews, :as => :reviewer, :dependent  => :delete_all
        has_many :reviews,
          :foreign_key => :reviewer_id,
          :class_name => options[:review_class].name        # Polymorphic has-many-through not supported (has_many :reviewables, :through => :reviews), so:
        # TODO: Implement with :join

        def reviewables(*args)
          query_options = args.extract_options!
          query_options[:include] = [:reviewable]
          query_options.reverse_merge!(:conditions => Support.polymorphic_conditions_for(self, :reviewer))
          
          ::Review.find(:all, query_options).collect! { |review| review.reviewable }
        end
      end
    end
  end
  
  # Assocations: Reviewable class (self) (e.g. Page).
  self.class_eval do
    has_many :reviews, :as => :reviewable, :dependent => :delete_all
    
    # Polymorphic has-many-through not supported (has_many :reviewers, :through => :reviews), so:
    # TODO: Implement with :join
    def reviewers(*args)
      query_options = args.extract_options!
      query_options[:include] = [:reviewer]
      query_options.reverse_merge!(:conditions => Support.polymorphic_conditions_for(self, :reviewable))
      
      ::Review.find(:all, query_options).collect! { |review| review.reviewer }
    end
    
    before_create :init_reviewable_caching_fields
    
    include ::IsReviewable::Reviewable::InstanceMethods
    extend  ::IsReviewable::Reviewable::Finders
  end
  
  # Save the initialized options for this class.
  self.write_inheritable_attribute :is_reviewable_options, options
  self.class_inheritable_reader :is_reviewable_options
end

#reviewable?Boolean Also known as: is_reviewable?

Checks if this object reviewable or not.


132
133
134
# File 'lib/is_reviewable/reviewable.rb', line 132

def reviewable?
  @@reviewable ||= self.respond_to?(:is_reviewable_options, true)
end

#reviewable_precisionObject Also known as: rating_precision

The rating value precision used for this reviewable class.

Using Rails default behaviour:

Float#round(<precision>)

150
151
152
# File 'lib/is_reviewable/reviewable.rb', line 150

def reviewable_precision
  self.is_reviewable_options[:total_precision]
end

#reviewable_scaleObject Also known as: rating_scale

The rating scale used for this reviewable class.


139
140
141
# File 'lib/is_reviewable/reviewable.rb', line 139

def reviewable_scale
  self.is_reviewable_options[:scale]
end