1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
module Fresco
  class CompositeSpecification
    def initialize(args = {})
      @specs = []
    end

    # Adds a new specification to the composite
    def add_specification(spec)
      @specs << spec
    end

    # Calls <tt>satisfied_by?</tt> on each specification with
    # the given object. Returns <tt>true</tt> if all of the specifications
    # pass, otherwise it returns <tt>false</tt>.
    def satisfied?
      @specs.collect{ |s| s.satisfied?}.grep(false).empty?
    end

    def unsatisfied_by
      @specs.find_all{|s| !s.satisfied?}
    end

  end
end


module Fresco

  class ProductComboSpecification < Fresco::CompositeSpecification

    def initialize(args = {})
      super
      subject = args[:subject]
      scheme_colors = args[:scheme_colors]

      self.add_specification(CategoryUniquenessSpecification.new(subject: subject))
      self.add_specification(ProductPerColorSpecification.new(subject: subject, colors: scheme_colors))
      self.add_specification(ProductCountSpecification.new(subject: subject, count: scheme_colors.count))
      self.add_specification(WearableSpecification.new(subject: subject))
    end

  end

end


module Fresco
  class ProductPerColorSpecification
    attr_reader :subject, :colors

    def initialize(args)
      @subject = args[:subject]
      @colors = args[:colors]
    end

    def satisfied?
      one_product_for_each_color?
    end

    private

    def one_product_for_each_color?
      self.colors.all?{|c| subject_colors.detect{|sc| sc == c}}
    end

    def subject_colors
      self.subject.map(&:q_color)
    end

  end
end


module Fresco
  class WearableSpecification
    attr_reader :subject

    def initialize(args)
      @subject = args[:subject]
    end

    def satisfied?
      subject_is_array? ? all_elements_wearable? : self.subject.wearable?
    end

    private

    def subject_is_array?
      self.subject.respond_to?(:each)
    end

    def all_elements_wearable?
      self.subject.all?{|s| s.wearable?}
    end

  end



module Fresco
  class CategoryUniquenessSpecification
    attr_reader :subject

    def initialize(args)
      @subject = args[:subject]
    end

    def satisfied?
      all_categories_are_unique?
    end

    private

    def categories
      self.subject.map{|s| s.category.name}
    end

    def all_categories_are_unique?
      categories.count == categories.uniq.count
    end
  end
end