casegen 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.rubocop.yml +109 -0
- data/.ruby-version +1 -1
- data/Gemfile +3 -1
- data/Gemfile.lock +51 -6
- data/README.md +10 -119
- data/Rakefile +9 -7
- data/bin/casegen +2 -1
- data/casegen.gemspec +13 -9
- data/doc/bounding_box.rb +37 -0
- data/doc/cart.rb +43 -0
- data/doc/expect_only.rb +28 -0
- data/doc/pricing.rb +50 -0
- data/doc/ruby_array.rb +41 -0
- data/lib/case_gen/combination.rb +38 -0
- data/lib/case_gen/combo_matcher.rb +15 -0
- data/lib/case_gen/exclude_rule.rb +50 -0
- data/lib/case_gen/expect_rule.rb +24 -0
- data/lib/case_gen/generator.rb +40 -0
- data/lib/case_gen/output/exclude.rb +6 -0
- data/lib/case_gen/output/exclude_as_table.rb +13 -0
- data/lib/case_gen/output/exclude_as_text.rb +12 -0
- data/lib/case_gen/output/exclude_inline.rb +13 -0
- data/lib/case_gen/output/exclude_inline_footnotes.rb +20 -0
- data/lib/case_gen/output.rb +66 -0
- data/lib/case_gen/rule_description.rb +11 -0
- data/lib/case_gen/set.rb +16 -0
- data/lib/casegen.rb +15 -183
- data/spec/cart_sample_spec.rb +46 -0
- data/spec/case_gen/combination_spec.rb +11 -0
- data/spec/case_gen/exclude_rule_spec.rb +17 -0
- data/spec/exclude_as_table_spec.rb +39 -0
- data/spec/exclude_as_text_spec.rb +58 -0
- data/spec/exclude_inline_footnotes_spec.rb +58 -0
- data/spec/exclude_inline_spec.rb +50 -0
- data/spec/expect_only_spec.rb +30 -0
- data/spec/spec_helper.rb +113 -0
- metadata +101 -35
- data/.idea/encodings.xml +0 -5
- data/.idea/misc.xml +0 -5
- data/.idea/modules.xml +0 -9
- data/.idea/vcs.xml +0 -7
- data/doc/calc.sample.txt +0 -13
- data/doc/cart.sample.rb +0 -3
- data/doc/cart.sample.txt +0 -33
- data/doc/ruby_array.sample.rb +0 -26
- data/lib/agents/sets/enum/by.rb +0 -244
- data/lib/agents/sets/enum/cluster.rb +0 -164
- data/lib/agents/sets/enum/inject.rb +0 -50
- data/lib/agents/sets/enum/nest.rb +0 -117
- data/lib/agents/sets/enum/op.rb +0 -283
- data/lib/agents/sets/enum/pipe.rb +0 -160
- data/lib/agents/sets/enum/tree.rb +0 -442
- data/lib/agents/sets.rb +0 -313
- data/test/agents/console_output_test.rb +0 -27
- data/test/agents/sets.test.rb +0 -227
- data/test/agents_test.rb +0 -41
- data/test/casegen.tests.rb +0 -0
- data/test/parser_test.rb +0 -163
- data/test/test_helper.rb +0 -2
    
        data/doc/cart.rb
    ADDED
    
    | @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative '../lib/casegen'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            sets = {
         | 
| 6 | 
            +
              payment: ['Credit', 'Check', 'Online Bank'],
         | 
| 7 | 
            +
              amount: [100, 1_000, 10_000],
         | 
| 8 | 
            +
              shipping: ['Ground', 'Air'],
         | 
| 9 | 
            +
              ship_to_country: ['US', 'Outside US'],
         | 
| 10 | 
            +
              bill_to_country: ['US', 'Outside US']
         | 
| 11 | 
            +
            }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            rules = {
         | 
| 14 | 
            +
              exclude: [
         | 
| 15 | 
            +
                {
         | 
| 16 | 
            +
                  criteria: %(shipping == "Ground" && ship_to_country == "Outside US" ),
         | 
| 17 | 
            +
                  description: 'Our ground shipper will only ship things within the US',
         | 
| 18 | 
            +
                },
         | 
| 19 | 
            +
                {
         | 
| 20 | 
            +
                  criteria: -> { payment == 'Check' && bill_to_country == 'Outside US' },
         | 
| 21 | 
            +
                  description: 'Our bank will not accept checks written from banks outside the US.',
         | 
| 22 | 
            +
                },
         | 
| 23 | 
            +
                {
         | 
| 24 | 
            +
                  criteria: -> { payment == 'Online Bank' && amount >= 1_000 },
         | 
| 25 | 
            +
                  description: <<~_,
         | 
| 26 | 
            +
                    While the online bank will process amounts > $1,000, we've experienced
         | 
| 27 | 
            +
                    occasional problems with their services and have had to write off some
         | 
| 28 | 
            +
                    transactions, so we no longer allow this payment option for amounts
         | 
| 29 | 
            +
                    greater than $1,000.
         | 
| 30 | 
            +
                  _
         | 
| 31 | 
            +
                },
         | 
| 32 | 
            +
                {
         | 
| 33 | 
            +
                  criteria: -> { ship_to_country == 'US' && bill_to_country == 'Outside US' },
         | 
| 34 | 
            +
                  description: "If we're shipping to the US, billing party cannot be outside US"
         | 
| 35 | 
            +
                },
         | 
| 36 | 
            +
              ]
         | 
| 37 | 
            +
            }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            if __FILE__ == $PROGRAM_NAME
         | 
| 40 | 
            +
              puts CaseGen.generate(sets, rules, :exclude_as_text).to_s
         | 
| 41 | 
            +
            else
         | 
| 42 | 
            +
              Fixtures.add(:cart, sets, rules)
         | 
| 43 | 
            +
            end
         | 
    
        data/doc/expect_only.rb
    ADDED
    
    | @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative '../lib/casegen'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            sets = {
         | 
| 6 | 
            +
              duration: [12, 24, 36, 60],
         | 
| 7 | 
            +
              unit: [60, 3600],
         | 
| 8 | 
            +
              result: [:expect]
         | 
| 9 | 
            +
            }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            rules = {
         | 
| 12 | 
            +
              expect: [
         | 
| 13 | 
            +
                {duration: 12, unit: 60, result: '12m'},
         | 
| 14 | 
            +
                {duration: 12, unit: 3600, result: '12h'},
         | 
| 15 | 
            +
                {duration: 24, unit: 60, result: '24m'},
         | 
| 16 | 
            +
                {duration: 24, unit: 3600, result: '1d'},
         | 
| 17 | 
            +
                {duration: 36, unit: 60, result: '36m'},
         | 
| 18 | 
            +
                {duration: 36, unit: 3600, result: '1d 12h'},
         | 
| 19 | 
            +
                {duration: 60, unit: 60, result: '1h'},
         | 
| 20 | 
            +
                {duration: 60, unit: 3600, result: '2d 12h'},
         | 
| 21 | 
            +
              ]
         | 
| 22 | 
            +
            }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            if __FILE__ == $PROGRAM_NAME
         | 
| 25 | 
            +
              puts CaseGen.generate(sets, rules, :exclude_inline).to_s
         | 
| 26 | 
            +
            else
         | 
| 27 | 
            +
              Fixtures.add(:duration, sets, rules)
         | 
| 28 | 
            +
            end
         | 
    
        data/doc/pricing.rb
    ADDED
    
    | @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative '../lib/casegen'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            sets = {
         | 
| 6 | 
            +
              subtotal: [25, 75, 200],
         | 
| 7 | 
            +
              discount: %w[0% 10% 20%],
         | 
| 8 | 
            +
              promo: %w[none apr fall],
         | 
| 9 | 
            +
              total: [:expect]
         | 
| 10 | 
            +
            }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            rules = {
         | 
| 13 | 
            +
              exclude: [
         | 
| 14 | 
            +
                {
         | 
| 15 | 
            +
                  criteria: %(subtotal < 100 && discount == '20%'),
         | 
| 16 | 
            +
                  description: 'Total must be above $100 to apply the 20% discount',
         | 
| 17 | 
            +
                },
         | 
| 18 | 
            +
                {
         | 
| 19 | 
            +
                  criteria: %((subtotal < 50) && discount == '10%'),
         | 
| 20 | 
            +
                  note: 'Total must be above $50 to apply the 10% discount',
         | 
| 21 | 
            +
                },
         | 
| 22 | 
            +
                {
         | 
| 23 | 
            +
                  criteria: %(discount != '20%' && subtotal == 200),
         | 
| 24 | 
            +
                  reason: 'Orders over 100 automatically get 20% discount',
         | 
| 25 | 
            +
                },
         | 
| 26 | 
            +
                {
         | 
| 27 | 
            +
                  criteria: %(discount != '10%' && subtotal == 75),
         | 
| 28 | 
            +
                  reason: 'Orders between 50 and 100 automatically get 10% discount',
         | 
| 29 | 
            +
                },
         | 
| 30 | 
            +
                {
         | 
| 31 | 
            +
                  criteria: %(discount == '20%' && promo != 'none'),
         | 
| 32 | 
            +
                  reason: '20% discount cannot be combined with promo',
         | 
| 33 | 
            +
                },
         | 
| 34 | 
            +
              ],
         | 
| 35 | 
            +
              expect: [
         | 
| 36 | 
            +
                {subtotal: 25, promo: 'none', total: '25.00'},
         | 
| 37 | 
            +
                {subtotal: 25, promo: 'apr', total: '17.50', reason: 'apr promo is 30%'},
         | 
| 38 | 
            +
                {subtotal: 25, promo: 'fall', total: '16.25', note: 'fall promo is 35%'},
         | 
| 39 | 
            +
                {subtotal: 75, promo: 'none', total: '67.50', note: '10% discount'},
         | 
| 40 | 
            +
                {subtotal: 75, promo: 'apr', total: '47.25', reason: '10% + apr promo is 30%'},
         | 
| 41 | 
            +
                {subtotal: 75, promo: 'fall', total: '43.88', note: '10% + fall promo is 35%'},
         | 
| 42 | 
            +
                {subtotal: 200, promo: 'none', total: '160.00', note: '20% discount'},
         | 
| 43 | 
            +
              ]
         | 
| 44 | 
            +
            }
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            if __FILE__ == $PROGRAM_NAME
         | 
| 47 | 
            +
              puts CaseGen.generate(sets, rules, :exclude_as_text)
         | 
| 48 | 
            +
            else
         | 
| 49 | 
            +
              Fixtures.add(:pricing, sets, rules)
         | 
| 50 | 
            +
            end
         | 
    
        data/doc/ruby_array.rb
    ADDED
    
    | @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative '../lib/case_gen'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            raise "This doesn't currently work. Do I even want to keep it?"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            # CLabs::CaseGen::CaseGen.new(DATA.read)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            # Outputs
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            # DataSubmitCase = Struct.new(:role, :authorization_code, :submit_enabled)
         | 
| 12 | 
            +
            #
         | 
| 13 | 
            +
            # cases = [DataSubmitCase.new("admin", "none", "true"),
         | 
| 14 | 
            +
            #          DataSubmitCase.new("admin", "invalid", "true"),
         | 
| 15 | 
            +
            #          DataSubmitCase.new("admin", "valid", "true"),
         | 
| 16 | 
            +
            #          DataSubmitCase.new("standard", "none", "false"),
         | 
| 17 | 
            +
            #          DataSubmitCase.new("standard", "invalid", "false"),
         | 
| 18 | 
            +
            #          DataSubmitCase.new("standard", "valid", "true")]
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            __END__
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            sets
         | 
| 23 | 
            +
            ----
         | 
| 24 | 
            +
            role: admin, standard
         | 
| 25 | 
            +
            authorization code: none, invalid, valid
         | 
| 26 | 
            +
            submit enabled: true, false
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            rules(sets)
         | 
| 29 | 
            +
            -----------
         | 
| 30 | 
            +
            exclude role = admin AND submit enabled = false
         | 
| 31 | 
            +
              Admin role can always submit
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            exclude role = standard AND authorization code = none AND submit enabled = true
         | 
| 34 | 
            +
            exclude role = standard AND authorization code = invalid AND submit enabled = true
         | 
| 35 | 
            +
            exclude role = standard AND authorization code = valid AND submit enabled = false
         | 
| 36 | 
            +
              Standard role can only submit when authorization code is valid
         | 
| 37 | 
            +
             | 
| 38 | 
            +
             | 
| 39 | 
            +
            ruby_array(rules)
         | 
| 40 | 
            +
            -------------
         | 
| 41 | 
            +
            DataSubmitCase
         | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module CaseGen
         | 
| 4 | 
            +
              class Combination
         | 
| 5 | 
            +
                attr_reader :names, :excluded_by_rule
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(hash_pairs)
         | 
| 8 | 
            +
                  @names = hash_pairs.map do |h|
         | 
| 9 | 
            +
                    k = h.first.first
         | 
| 10 | 
            +
                    v = h.first.last
         | 
| 11 | 
            +
                    append(k, v)
         | 
| 12 | 
            +
                    k
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def hash_row
         | 
| 17 | 
            +
                  {}.tap do |h|
         | 
| 18 | 
            +
                    @names.each do |ivar|
         | 
| 19 | 
            +
                      h[ivar] = instance_variable_get("@#{ivar}")
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def append(key, value)
         | 
| 25 | 
            +
                  @names << key if defined?(@names)
         | 
| 26 | 
            +
                  instance_variable_set("@#{key}", value)
         | 
| 27 | 
            +
                  self.class.attr_accessor key
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def exclude_with(rule)
         | 
| 31 | 
            +
                  @excluded_by_rule = rule
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def excluded?
         | 
| 35 | 
            +
                  !@excluded_by_rule.nil?
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
            end
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module CaseGen
         | 
| 4 | 
            +
              module ComboMatcher
         | 
| 5 | 
            +
                def matches_criteria(combo, additional_ignore_keys = [])
         | 
| 6 | 
            +
                  criteria_keys = (@rule_data.keys - additional_ignore_keys) - ignore_keys
         | 
| 7 | 
            +
                  criteria = @rule_data.slice(*criteria_keys)
         | 
| 8 | 
            +
                  criteria == combo.hash_row.slice(*criteria_keys)
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def ignore_keys
         | 
| 12 | 
            +
                  %i[description reason note index]
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module CaseGen
         | 
| 4 | 
            +
              class ExcludeRule
         | 
| 5 | 
            +
                include ComboMatcher
         | 
| 6 | 
            +
                include RuleDescription
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                attr_reader :rule_data, :description
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def initialize(rule_data)
         | 
| 11 | 
            +
                  @rule_data = rule_data
         | 
| 12 | 
            +
                  @description = rule_description(rule_data)
         | 
| 13 | 
            +
                  @criteria = rule_data[:criteria]
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def apply(combos)
         | 
| 17 | 
            +
                  matches = combos.select do |combo|
         | 
| 18 | 
            +
                    case @criteria
         | 
| 19 | 
            +
                    when String
         | 
| 20 | 
            +
                      combo.instance_eval(@criteria)
         | 
| 21 | 
            +
                    when Proc
         | 
| 22 | 
            +
                      combo.instance_exec(&@criteria)
         | 
| 23 | 
            +
                    when nil
         | 
| 24 | 
            +
                      # if the rule data has keys matching the combo, then compare the
         | 
| 25 | 
            +
                      # values of provided keys.
         | 
| 26 | 
            +
                      matches_criteria(combo)
         | 
| 27 | 
            +
                    else
         | 
| 28 | 
            +
                      raise "Unknown rule criteria class: #{@criteria.class}"
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  process_matches(combos, matches)
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                private
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def process_matches(combos, matches)
         | 
| 38 | 
            +
                  combos.each do |combo|
         | 
| 39 | 
            +
                    next unless matches.include?(combo)
         | 
| 40 | 
            +
                    next if combo.excluded?
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    combo.exclude_with(self)
         | 
| 43 | 
            +
                    expect_keys = combo.names.select { |name| combo.send(name) == :expect }
         | 
| 44 | 
            +
                    expect_keys.each do |expect_key|
         | 
| 45 | 
            +
                      combo.send("#{expect_key}=", '')
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module CaseGen
         | 
| 4 | 
            +
              class ExpectRule
         | 
| 5 | 
            +
                include ComboMatcher
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(rule_data)
         | 
| 8 | 
            +
                  @rule_data = rule_data
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def apply(combos)
         | 
| 12 | 
            +
                  combos.each do |combo|
         | 
| 13 | 
            +
                    expect_keys = combo.names.select { |name| combo.send(name) == :expect }
         | 
| 14 | 
            +
                    next if expect_keys.none?
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    next unless matches_criteria(combo, expect_keys)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    expect_keys.each do |expect_key|
         | 
| 19 | 
            +
                      combo.send("#{expect_key}=", @rule_data[expect_key])
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'tablesmith'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module CaseGen
         | 
| 6 | 
            +
              class Generator
         | 
| 7 | 
            +
                attr_reader :sets, :rules, :combos, :exclusions
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize(sets, rules)
         | 
| 10 | 
            +
                  @sets = sets.map do |title, values|
         | 
| 11 | 
            +
                    CaseGen::Set.new(title, values)
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                  @rules = rules
         | 
| 14 | 
            +
                  @combos = generate_combinations
         | 
| 15 | 
            +
                  apply_rules
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                private
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def generate_combinations
         | 
| 21 | 
            +
                  hash_pairs = @sets.map(&:hash_pairs)
         | 
| 22 | 
            +
                  product_of(hash_pairs).map { |c| Combination.new(c) }
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def product_of(sets)
         | 
| 26 | 
            +
                  head, *rest = sets
         | 
| 27 | 
            +
                  head.product(*rest)
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def apply_rules
         | 
| 31 | 
            +
                  @rules.each do |type, rules|
         | 
| 32 | 
            +
                    klass = CaseGen.const_get("#{type.to_s.capitalize}Rule")
         | 
| 33 | 
            +
                    rules.each_with_index do |rule_data, idx|
         | 
| 34 | 
            +
                      rule_data[:index] = idx + 1
         | 
| 35 | 
            +
                      klass.new(rule_data).apply(@combos)
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
| @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module CaseGen
         | 
| 4 | 
            +
              class ExcludeAsText < CaseGen::Output
         | 
| 5 | 
            +
                def exclude_output(_)
         | 
| 6 | 
            +
                  body = @generator.rules[:exclude].map do |rule|
         | 
| 7 | 
            +
                    [rule[:criteria], "  #{rule_description(rule)}", '']
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
                  (header + body).join("\n")
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
            end
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module CaseGen
         | 
| 4 | 
            +
              class ExcludeInlineFootnotes < CaseGen::Output
         | 
| 5 | 
            +
                def partition_exclusions?
         | 
| 6 | 
            +
                  false
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def exclude_description(rule)
         | 
| 10 | 
            +
                  "[#{rule.rule_data[:index]}]"
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def exclude_output(_)
         | 
| 14 | 
            +
                  body = @generator.rules[:exclude].map do |rule|
         | 
| 15 | 
            +
                    ["[#{rule[:index]}] #{rule_description(rule)}"]
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                  (header + body).join("\n")
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         | 
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module CaseGen
         | 
| 4 | 
            +
              class Output
         | 
| 5 | 
            +
                def self.create(generator, output_type = :exclude)
         | 
| 6 | 
            +
                  klass_name = output_type.to_s.split('_').map(&:capitalize).join.to_s
         | 
| 7 | 
            +
                  Object.const_get("CaseGen::#{klass_name}").new(generator)
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                include RuleDescription
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def initialize(generator)
         | 
| 13 | 
            +
                  @generator = generator
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def to_s
         | 
| 17 | 
            +
                  update_excluded_values
         | 
| 18 | 
            +
                  include, exclude = partition_exclusions
         | 
| 19 | 
            +
                  as_table(include).tap { |o| o << exclude_output(exclude) }
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                private
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def exclude_output(_exclude)
         | 
| 25 | 
            +
                  ''
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def as_table(combos)
         | 
| 29 | 
            +
                  combos.map(&:hash_row).to_table.to_s
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def update_excluded_values
         | 
| 33 | 
            +
                  @generator.combos.each do |combo|
         | 
| 34 | 
            +
                    if combo.excluded?
         | 
| 35 | 
            +
                      value = exclude_description(combo.excluded_by_rule)
         | 
| 36 | 
            +
                      combo.append(:exclude, value)
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def partition_exclusions
         | 
| 42 | 
            +
                  combos = @generator.combos
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  if partition_exclusions?
         | 
| 45 | 
            +
                    exclude, include = combos.partition(&:excluded?)
         | 
| 46 | 
            +
                    [include, exclude]
         | 
| 47 | 
            +
                  else
         | 
| 48 | 
            +
                    [combos, []]
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def partition_exclusions?
         | 
| 53 | 
            +
                  true
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def exclude_description(_rule)
         | 
| 57 | 
            +
                  nil
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def header
         | 
| 61 | 
            +
                  ['', 'exclude', '-------']
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
            end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            Dir[File.join(__dir__, 'output', '*.rb')].sort.each { |fn| require fn }
         |