factbase 0.0.41 → 0.0.42
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/Gemfile +1 -1
- data/Gemfile.lock +2 -2
- data/README.md +1 -0
- data/lib/factbase/inv.rb +4 -4
- data/lib/factbase/looged.rb +4 -4
- data/lib/factbase/rules.rb +26 -11
- data/lib/factbase/term.rb +24 -10
- data/lib/factbase/to_json.rb +1 -1
- data/lib/factbase/to_xml.rb +1 -1
- data/lib/factbase/to_yaml.rb +1 -1
- data/lib/factbase/tuples.rb +3 -4
- data/lib/factbase.rb +1 -1
- data/test/factbase/test_query.rb +1 -0
- data/test/factbase/test_rules.rb +41 -0
- data/test/factbase/test_term.rb +28 -0
- data/test/factbase/test_to_json.rb +10 -0
- data/test/factbase/test_to_xml.rb +11 -0
- data/test/factbase/test_to_yaml.rb +10 -0
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 42066d9d82538ecb6f0ec8df6cc711f4328e1ba496f00366fe67719ca8a1185e
         | 
| 4 | 
            +
              data.tar.gz: d40e21b7e426af5184b9b03f515da4508b1e1e9e52995f566b16c778eefefca3
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: ff04d1f7e3eb1b18b36544ea8fb61280cda250a5e9920ba7b858092ae7c13111efce537d39acd7119f2f70c8704a89d06c98bf54b5a4639b4f187599f61eecbc
         | 
| 7 | 
            +
              data.tar.gz: '09ba7d04f933f758c1db716c9490d464a2202644560a594bfae0a77e8bee8d26dcc38605101417a062ccd5c9645b5187e7f069e8f31e6bc782e48dc79775a347'
         | 
    
        data/Gemfile
    CHANGED
    
    | @@ -28,7 +28,7 @@ gem 'rake', '13.2.1', require: false | |
| 28 28 | 
             
            gem 'rspec-rails', '6.1.2', require: false
         | 
| 29 29 | 
             
            gem 'rubocop', '1.64.1', require: false
         | 
| 30 30 | 
             
            gem 'rubocop-performance', '1.21.0', require: false
         | 
| 31 | 
            -
            gem 'rubocop-rspec', '2. | 
| 31 | 
            +
            gem 'rubocop-rspec', '2.30.0', require: false
         | 
| 32 32 | 
             
            gem 'simplecov', '0.22.0', require: false
         | 
| 33 33 | 
             
            gem 'simplecov-cobertura', '2.1.0', require: false
         | 
| 34 34 | 
             
            gem 'yard', '0.9.36', require: false
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -145,7 +145,7 @@ GEM | |
| 145 145 | 
             
                rubocop-performance (1.21.0)
         | 
| 146 146 | 
             
                  rubocop (>= 1.48.1, < 2.0)
         | 
| 147 147 | 
             
                  rubocop-ast (>= 1.31.1, < 2.0)
         | 
| 148 | 
            -
                rubocop-rspec (2. | 
| 148 | 
            +
                rubocop-rspec (2.30.0)
         | 
| 149 149 | 
             
                  rubocop (~> 1.40)
         | 
| 150 150 | 
             
                  rubocop-capybara (~> 2.17)
         | 
| 151 151 | 
             
                  rubocop-factory_bot (~> 2.22)
         | 
| @@ -186,7 +186,7 @@ DEPENDENCIES | |
| 186 186 | 
             
              rspec-rails (= 6.1.2)
         | 
| 187 187 | 
             
              rubocop (= 1.64.1)
         | 
| 188 188 | 
             
              rubocop-performance (= 1.21.0)
         | 
| 189 | 
            -
              rubocop-rspec (= 2. | 
| 189 | 
            +
              rubocop-rspec (= 2.30.0)
         | 
| 190 190 | 
             
              simplecov (= 0.22.0)
         | 
| 191 191 | 
             
              simplecov-cobertura (= 2.1.0)
         | 
| 192 192 | 
             
              yard (= 0.9.36)
         | 
    
        data/README.md
    CHANGED
    
    | @@ -88,6 +88,7 @@ Also, some simple arithmetic: | |
| 88 88 | 
             
            One term is for meta-programming:
         | 
| 89 89 |  | 
| 90 90 | 
             
            * `(defn foo "self.to_s")` defines a new term using Ruby syntax and returns true
         | 
| 91 | 
            +
            * `(undef foo)` undefines a term (nothing happens if it's not defined yet)
         | 
| 91 92 |  | 
| 92 93 | 
             
            There are terms that are history of search aware:
         | 
| 93 94 |  | 
    
        data/lib/factbase/inv.rb
    CHANGED
    
    | @@ -81,13 +81,13 @@ class Factbase::Inv | |
| 81 81 | 
             
                end
         | 
| 82 82 |  | 
| 83 83 | 
             
                # rubocop:disable Style/OptionalBooleanParameter
         | 
| 84 | 
            -
                def respond_to?( | 
| 84 | 
            +
                def respond_to?(_method, _include_private = false)
         | 
| 85 85 | 
             
                  # rubocop:enable Style/OptionalBooleanParameter
         | 
| 86 | 
            -
                   | 
| 86 | 
            +
                  true
         | 
| 87 87 | 
             
                end
         | 
| 88 88 |  | 
| 89 | 
            -
                def respond_to_missing?( | 
| 90 | 
            -
                   | 
| 89 | 
            +
                def respond_to_missing?(_method, _include_private = false)
         | 
| 90 | 
            +
                  true
         | 
| 91 91 | 
             
                end
         | 
| 92 92 | 
             
              end
         | 
| 93 93 |  | 
    
        data/lib/factbase/looged.rb
    CHANGED
    
    | @@ -91,13 +91,13 @@ class Factbase::Looged | |
| 91 91 | 
             
                end
         | 
| 92 92 |  | 
| 93 93 | 
             
                # rubocop:disable Style/OptionalBooleanParameter
         | 
| 94 | 
            -
                def respond_to?( | 
| 94 | 
            +
                def respond_to?(_method, _include_private = false)
         | 
| 95 95 | 
             
                  # rubocop:enable Style/OptionalBooleanParameter
         | 
| 96 | 
            -
                   | 
| 96 | 
            +
                  true
         | 
| 97 97 | 
             
                end
         | 
| 98 98 |  | 
| 99 | 
            -
                def respond_to_missing?( | 
| 100 | 
            -
                   | 
| 99 | 
            +
                def respond_to_missing?(_method, _include_private = false)
         | 
| 100 | 
            +
                  true
         | 
| 101 101 | 
             
                end
         | 
| 102 102 | 
             
              end
         | 
| 103 103 |  | 
    
        data/lib/factbase/rules.rb
    CHANGED
    
    | @@ -21,20 +21,21 @@ | |
| 21 21 | 
             
            # SOFTWARE.
         | 
| 22 22 |  | 
| 23 23 | 
             
            require_relative '../factbase'
         | 
| 24 | 
            +
            require_relative '../factbase/syntax'
         | 
| 24 25 |  | 
| 25 26 | 
             
            # A decorator of a Factbase, that checks rules on every set.
         | 
| 26 27 | 
             
            # Author:: Yegor Bugayenko (yegor256@gmail.com)
         | 
| 27 28 | 
             
            # Copyright:: Copyright (c) 2024 Yegor Bugayenko
         | 
| 28 29 | 
             
            # License:: MIT
         | 
| 29 30 | 
             
            class Factbase::Rules
         | 
| 30 | 
            -
              def initialize(fb, rules)
         | 
| 31 | 
            +
              def initialize(fb, rules, check = Check.new(rules))
         | 
| 31 32 | 
             
                @fb = fb
         | 
| 32 33 | 
             
                @rules = rules
         | 
| 33 | 
            -
                @check =  | 
| 34 | 
            +
                @check = check
         | 
| 34 35 | 
             
              end
         | 
| 35 36 |  | 
| 36 37 | 
             
              def dup
         | 
| 37 | 
            -
                Factbase::Rules.new(@fb.dup, @rules)
         | 
| 38 | 
            +
                Factbase::Rules.new(@fb.dup, @rules, @check)
         | 
| 38 39 | 
             
              end
         | 
| 39 40 |  | 
| 40 41 | 
             
              def size
         | 
| @@ -50,7 +51,13 @@ class Factbase::Rules | |
| 50 51 | 
             
              end
         | 
| 51 52 |  | 
| 52 53 | 
             
              def txn(this = self, &)
         | 
| 54 | 
            +
                before = @check
         | 
| 55 | 
            +
                @check = Blind.new
         | 
| 53 56 | 
             
                @fb.txn(this, &)
         | 
| 57 | 
            +
                @check = before
         | 
| 58 | 
            +
                @fb.query('(always)').each do |f|
         | 
| 59 | 
            +
                  @check.it(f)
         | 
| 60 | 
            +
                end
         | 
| 54 61 | 
             
              end
         | 
| 55 62 |  | 
| 56 63 | 
             
              def export
         | 
| @@ -83,13 +90,13 @@ class Factbase::Rules | |
| 83 90 | 
             
                end
         | 
| 84 91 |  | 
| 85 92 | 
             
                # rubocop:disable Style/OptionalBooleanParameter
         | 
| 86 | 
            -
                def respond_to?( | 
| 93 | 
            +
                def respond_to?(_method, _include_private = false)
         | 
| 87 94 | 
             
                  # rubocop:enable Style/OptionalBooleanParameter
         | 
| 88 | 
            -
                   | 
| 95 | 
            +
                  true
         | 
| 89 96 | 
             
                end
         | 
| 90 97 |  | 
| 91 | 
            -
                def respond_to_missing?( | 
| 92 | 
            -
                   | 
| 98 | 
            +
                def respond_to_missing?(_method, _include_private = false)
         | 
| 99 | 
            +
                  true
         | 
| 93 100 | 
             
                end
         | 
| 94 101 | 
             
              end
         | 
| 95 102 |  | 
| @@ -118,16 +125,24 @@ class Factbase::Rules | |
| 118 125 | 
             
              # Check one fact.
         | 
| 119 126 | 
             
              #
         | 
| 120 127 | 
             
              # This is an internal class, it is not supposed to be instantiated directly.
         | 
| 121 | 
            -
              #
         | 
| 122 128 | 
             
              class Check
         | 
| 123 | 
            -
                def initialize( | 
| 124 | 
            -
                  @fb = fb
         | 
| 129 | 
            +
                def initialize(expr)
         | 
| 125 130 | 
             
                  @expr = expr
         | 
| 126 131 | 
             
                end
         | 
| 127 132 |  | 
| 128 133 | 
             
                def it(fact)
         | 
| 129 134 | 
             
                  return if Factbase::Syntax.new(@expr).to_term.evaluate(fact, [])
         | 
| 130 | 
            -
                   | 
| 135 | 
            +
                  e = "#{@expr[0..32]}..." if @expr.length > 32
         | 
| 136 | 
            +
                  raise "The fact doesn't match the #{e.inspect} rule: #{fact}"
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
              end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
              # Check one fact (never complaining).
         | 
| 141 | 
            +
              #
         | 
| 142 | 
            +
              # This is an internal class, it is not supposed to be instantiated directly.
         | 
| 143 | 
            +
              class Blind
         | 
| 144 | 
            +
                def it(_fact)
         | 
| 145 | 
            +
                  true
         | 
| 131 146 | 
             
                end
         | 
| 132 147 | 
             
              end
         | 
| 133 148 | 
             
            end
         | 
    
        data/lib/factbase/term.rb
    CHANGED
    
    | @@ -68,7 +68,7 @@ class Factbase::Term | |
| 68 68 | 
             
              rescue NoMethodError => e
         | 
| 69 69 | 
             
                raise "Term '#{@op}' is not defined at #{self}: #{e.message}"
         | 
| 70 70 | 
             
              rescue StandardError => e
         | 
| 71 | 
            -
                raise "#{e.message} at #{self}"
         | 
| 71 | 
            +
                raise "#{e.message} at #{self} (#{e.backtrace[0]})"
         | 
| 72 72 | 
             
              end
         | 
| 73 73 |  | 
| 74 74 | 
             
              # Simplify it if possible.
         | 
| @@ -118,14 +118,14 @@ class Factbase::Term | |
| 118 118 |  | 
| 119 119 | 
             
              def or(fact, maps)
         | 
| 120 120 | 
             
                (0..@operands.size - 1).each do |i|
         | 
| 121 | 
            -
                  return true if only_bool(the_values(i, fact, maps),  | 
| 121 | 
            +
                  return true if only_bool(the_values(i, fact, maps), i)
         | 
| 122 122 | 
             
                end
         | 
| 123 123 | 
             
                false
         | 
| 124 124 | 
             
              end
         | 
| 125 125 |  | 
| 126 126 | 
             
              def and(fact, maps)
         | 
| 127 127 | 
             
                (0..@operands.size - 1).each do |i|
         | 
| 128 | 
            -
                  return false unless only_bool(the_values(i, fact, maps),  | 
| 128 | 
            +
                  return false unless only_bool(the_values(i, fact, maps), i)
         | 
| 129 129 | 
             
                end
         | 
| 130 130 | 
             
                true
         | 
| 131 131 | 
             
              end
         | 
| @@ -179,7 +179,7 @@ class Factbase::Term | |
| 179 179 | 
             
              def at(fact, maps)
         | 
| 180 180 | 
             
                assert_args(2)
         | 
| 181 181 | 
             
                i = the_values(0, fact, maps)
         | 
| 182 | 
            -
                raise  | 
| 182 | 
            +
                raise "Too many values (#{i.size}) at first position, one expected" unless i.size == 1
         | 
| 183 183 | 
             
                i = i[0]
         | 
| 184 184 | 
             
                return nil if i.nil?
         | 
| 185 185 | 
             
                v = the_values(1, fact, maps)
         | 
| @@ -301,8 +301,12 @@ class Factbase::Term | |
| 301 301 | 
             
              end
         | 
| 302 302 |  | 
| 303 303 | 
             
              def defn(_fact, _maps)
         | 
| 304 | 
            +
                assert_args(2)
         | 
| 304 305 | 
             
                fn = @operands[0]
         | 
| 305 | 
            -
                raise  | 
| 306 | 
            +
                raise "A symbol expected as first argument of 'defn'" unless fn.is_a?(Symbol)
         | 
| 307 | 
            +
                raise "Can't use '#{fn}' name as a term" if Factbase::Term.instance_methods(true).include?(fn)
         | 
| 308 | 
            +
                raise "Term '#{fn}' is already defined" if Factbase::Term.private_instance_methods(false).include?(fn)
         | 
| 309 | 
            +
                raise "The '#{fn}' is a bad name for a term" unless fn.match?(/^[a-z_]+$/)
         | 
| 306 310 | 
             
                e = "class Factbase::Term\nprivate\ndef #{fn}(fact, maps)\n#{@operands[1]}\nend\nend"
         | 
| 307 311 | 
             
                # rubocop:disable Security/Eval
         | 
| 308 312 | 
             
                eval(e)
         | 
| @@ -310,6 +314,16 @@ class Factbase::Term | |
| 310 314 | 
             
                true
         | 
| 311 315 | 
             
              end
         | 
| 312 316 |  | 
| 317 | 
            +
              def undef(_fact, _maps)
         | 
| 318 | 
            +
                assert_args(1)
         | 
| 319 | 
            +
                fn = @operands[0]
         | 
| 320 | 
            +
                raise "A symbol expected as first argument of 'undef'" unless fn.is_a?(Symbol)
         | 
| 321 | 
            +
                if Factbase::Term.private_instance_methods(false).include?(fn)
         | 
| 322 | 
            +
                  Factbase::Term.class_eval("undef :#{fn}", __FILE__, __LINE__ - 1) # undef :foo
         | 
| 323 | 
            +
                end
         | 
| 324 | 
            +
                true
         | 
| 325 | 
            +
              end
         | 
| 326 | 
            +
             | 
| 313 327 | 
             
              def min(_fact, maps)
         | 
| 314 328 | 
             
                @min ||= best(maps) { |v, b| v < b }
         | 
| 315 329 | 
             
              end
         | 
| @@ -326,7 +340,7 @@ class Factbase::Term | |
| 326 340 | 
             
                @sum ||=
         | 
| 327 341 | 
             
                  begin
         | 
| 328 342 | 
             
                    k = @operands[0]
         | 
| 329 | 
            -
                    raise "A symbol expected, but  | 
| 343 | 
            +
                    raise "A symbol expected, but '#{k}' provided" unless k.is_a?(Symbol)
         | 
| 330 344 | 
             
                    sum = 0
         | 
| 331 345 | 
             
                    maps.each do |m|
         | 
| 332 346 | 
             
                      vv = m[k.to_s]
         | 
| @@ -343,9 +357,9 @@ class Factbase::Term | |
| 343 357 | 
             
              def agg(_fact, maps)
         | 
| 344 358 | 
             
                assert_args(2)
         | 
| 345 359 | 
             
                selector = @operands[0]
         | 
| 346 | 
            -
                raise "A term expected, but #{selector} provided" unless selector.is_a?(Factbase::Term)
         | 
| 360 | 
            +
                raise "A term expected, but '#{selector}' provided" unless selector.is_a?(Factbase::Term)
         | 
| 347 361 | 
             
                term = @operands[1]
         | 
| 348 | 
            -
                raise "A term expected, but #{term} provided" unless term.is_a?(Factbase::Term)
         | 
| 362 | 
            +
                raise "A term expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
         | 
| 349 363 | 
             
                subset = maps.select { |m| selector.evaluate(m, maps) }
         | 
| 350 364 | 
             
                @agg ||=
         | 
| 351 365 | 
             
                  if subset.empty?
         | 
| @@ -363,7 +377,7 @@ class Factbase::Term | |
| 363 377 |  | 
| 364 378 | 
             
              def by_symbol(pos, fact)
         | 
| 365 379 | 
             
                o = @operands[pos]
         | 
| 366 | 
            -
                raise "A symbol expected at ##{pos}, but  | 
| 380 | 
            +
                raise "A symbol expected at ##{pos}, but '#{o}' provided" unless o.is_a?(Symbol)
         | 
| 367 381 | 
             
                k = o.to_s
         | 
| 368 382 | 
             
                fact[k]
         | 
| 369 383 | 
             
              end
         | 
| @@ -388,7 +402,7 @@ class Factbase::Term | |
| 388 402 |  | 
| 389 403 | 
             
              def best(maps)
         | 
| 390 404 | 
             
                k = @operands[0]
         | 
| 391 | 
            -
                raise "A symbol expected, but  | 
| 405 | 
            +
                raise "A symbol expected, but #{k} provided" unless k.is_a?(Symbol)
         | 
| 392 406 | 
             
                best = nil
         | 
| 393 407 | 
             
                maps.each do |m|
         | 
| 394 408 | 
             
                  vv = m[k.to_s]
         | 
    
        data/lib/factbase/to_json.rb
    CHANGED
    
    
    
        data/lib/factbase/to_xml.rb
    CHANGED
    
    
    
        data/lib/factbase/to_yaml.rb
    CHANGED
    
    
    
        data/lib/factbase/tuples.rb
    CHANGED
    
    | @@ -22,8 +22,6 @@ | |
| 22 22 |  | 
| 23 23 | 
             
            require_relative '../factbase'
         | 
| 24 24 |  | 
| 25 | 
            -
            # Tuples.
         | 
| 26 | 
            -
            #
         | 
| 27 25 | 
             
            # With the help of this class, it's possible to select a few facts
         | 
| 28 26 | 
             
            # from a factbase at a time, which depend on each other. For example,
         | 
| 29 27 | 
             
            # it's necessary to find a fact where the +name+ is set and then find
         | 
| @@ -36,8 +34,9 @@ require_relative '../factbase' | |
| 36 34 | 
             
            #  end
         | 
| 37 35 | 
             
            #
         | 
| 38 36 | 
             
            # Here, the +{f0.salary}+ is a special substitution place, which is replaced
         | 
| 39 | 
            -
            # by the +salary+ of the fact that is found by the previous query. | 
| 40 | 
            -
            # | 
| 37 | 
            +
            # by the +salary+ of the fact that is found by the previous query.
         | 
| 38 | 
            +
            #
         | 
| 39 | 
            +
            # The indexing of queries starts from zero.
         | 
| 41 40 | 
             
            #
         | 
| 42 41 | 
             
            # Author:: Yegor Bugayenko (yegor256@gmail.com)
         | 
| 43 42 | 
             
            # Copyright:: Copyright (c) 2024 Yegor Bugayenko
         | 
    
        data/lib/factbase.rb
    CHANGED
    
    | @@ -79,7 +79,7 @@ require 'yaml' | |
| 79 79 | 
             
            # License:: MIT
         | 
| 80 80 | 
             
            class Factbase
         | 
| 81 81 | 
             
              # Current version of the gem (changed by .rultor.yml on every release)
         | 
| 82 | 
            -
              VERSION = '0.0. | 
| 82 | 
            +
              VERSION = '0.0.42'
         | 
| 83 83 |  | 
| 84 84 | 
             
              # An exception that may be thrown in a transaction, to roll it back.
         | 
| 85 85 | 
             
              class Rollback < StandardError; end
         | 
    
        data/test/factbase/test_query.rb
    CHANGED
    
    | @@ -79,6 +79,7 @@ class TestQuery < Minitest::Test | |
| 79 79 | 
             
                  '(eq time (min time))' => 1,
         | 
| 80 80 | 
             
                  '(and (absent time) (exists pi))' => 1,
         | 
| 81 81 | 
             
                  "(and (exists time) (not (\t\texists pi)))" => 1,
         | 
| 82 | 
            +
                  '(undef something)' => 3,
         | 
| 82 83 | 
             
                  "(or (eq num +66) (lt time #{(Time.now - 200).utc.iso8601}))" => 1
         | 
| 83 84 | 
             
                }.each do |q, r|
         | 
| 84 85 | 
             
                  assert_equal(r, Factbase::Query.new(maps, Mutex.new, q).each.to_a.size, q)
         | 
    
        data/test/factbase/test_rules.rb
    CHANGED
    
    | @@ -42,4 +42,45 @@ class TestRules < Minitest::Test | |
| 42 42 | 
             
                  f2.first = 1
         | 
| 43 43 | 
             
                end
         | 
| 44 44 | 
             
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def test_to_string
         | 
| 47 | 
            +
                fb = Factbase::Rules.new(
         | 
| 48 | 
            +
                  Factbase.new,
         | 
| 49 | 
            +
                  '(when (exists a) (exists b))'
         | 
| 50 | 
            +
                )
         | 
| 51 | 
            +
                f = fb.insert
         | 
| 52 | 
            +
                assert(f.to_s.length.positive?)
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              def test_check_only_when_txn_is_closed
         | 
| 56 | 
            +
                fb = Factbase::Rules.new(Factbase.new, '(when (exists a) (exists b))')
         | 
| 57 | 
            +
                ok = false
         | 
| 58 | 
            +
                assert_raises do
         | 
| 59 | 
            +
                  fb.txn do |fbt|
         | 
| 60 | 
            +
                    f = fbt.insert
         | 
| 61 | 
            +
                    f.a = 1
         | 
| 62 | 
            +
                    f.c = 2
         | 
| 63 | 
            +
                    ok = true
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
                assert(ok)
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              def test_in_combination_with_pre
         | 
| 70 | 
            +
                fb = Factbase::Rules.new(Factbase.new, '(when (exists a) (exists b))')
         | 
| 71 | 
            +
                fb = Factbase::Pre.new(fb) do |f|
         | 
| 72 | 
            +
                  f.hello = 42
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
                ok = false
         | 
| 75 | 
            +
                assert_raises do
         | 
| 76 | 
            +
                  fb.txn do |fbt|
         | 
| 77 | 
            +
                    f = fbt.insert
         | 
| 78 | 
            +
                    f.a = 1
         | 
| 79 | 
            +
                    f.c = 2
         | 
| 80 | 
            +
                    ok = true
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
                assert(ok)
         | 
| 84 | 
            +
                assert_equal(1, fb.query('(eq hello 42)').each.to_a.size)
         | 
| 85 | 
            +
              end
         | 
| 45 86 | 
             
            end
         | 
    
        data/test/factbase/test_term.rb
    CHANGED
    
    | @@ -166,6 +166,34 @@ class TestTerm < Minitest::Test | |
| 166 166 | 
             
                assert_equal('(foo \'hello, world!\')', t1.evaluate(fact, []))
         | 
| 167 167 | 
             
              end
         | 
| 168 168 |  | 
| 169 | 
            +
              def test_defn_again_by_mistake
         | 
| 170 | 
            +
                t = Factbase::Term.new(:defn, [:and, 'self.to_s'])
         | 
| 171 | 
            +
                assert_raises do
         | 
| 172 | 
            +
                  t.evaluate(fact, [])
         | 
| 173 | 
            +
                end
         | 
| 174 | 
            +
              end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
              def test_defn_bad_name_by_mistake
         | 
| 177 | 
            +
                t = Factbase::Term.new(:defn, [:to_s, 'self.to_s'])
         | 
| 178 | 
            +
                assert_raises do
         | 
| 179 | 
            +
                  t.evaluate(fact, [])
         | 
| 180 | 
            +
                end
         | 
| 181 | 
            +
              end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
              def test_defn_bad_name_spelling_by_mistake
         | 
| 184 | 
            +
                t = Factbase::Term.new(:defn, [:'some-key', 'self.to_s'])
         | 
| 185 | 
            +
                assert_raises do
         | 
| 186 | 
            +
                  t.evaluate(fact, [])
         | 
| 187 | 
            +
                end
         | 
| 188 | 
            +
              end
         | 
| 189 | 
            +
             | 
| 190 | 
            +
              def test_undef_simple
         | 
| 191 | 
            +
                t = Factbase::Term.new(:defn, [:hello, 'self.to_s'])
         | 
| 192 | 
            +
                assert_equal(true, t.evaluate(fact, []))
         | 
| 193 | 
            +
                t = Factbase::Term.new(:undef, [:hello])
         | 
| 194 | 
            +
                assert_equal(true, t.evaluate(fact, []))
         | 
| 195 | 
            +
              end
         | 
| 196 | 
            +
             | 
| 169 197 | 
             
              def test_past
         | 
| 170 198 | 
             
                t = Factbase::Term.new(:prev, [:foo])
         | 
| 171 199 | 
             
                assert_nil(t.evaluate(fact('foo' => 4), []))
         | 
| @@ -39,4 +39,14 @@ class TestToJSON < Minitest::Test | |
| 39 39 | 
             
                json = JSON.parse(to.json)
         | 
| 40 40 | 
             
                assert(42, json[0]['foo'][1])
         | 
| 41 41 | 
             
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              def test_sort_keys
         | 
| 44 | 
            +
                fb = Factbase.new
         | 
| 45 | 
            +
                f = fb.insert
         | 
| 46 | 
            +
                f.c = 42
         | 
| 47 | 
            +
                f.b = 1
         | 
| 48 | 
            +
                f.a = 256
         | 
| 49 | 
            +
                json = Factbase::ToJSON.new(fb).json
         | 
| 50 | 
            +
                assert(json.include?('{"a":256,"b":1,"c":42}'), json)
         | 
| 51 | 
            +
              end
         | 
| 42 52 | 
             
            end
         | 
| @@ -73,4 +73,15 @@ class TestToXML < Minitest::Test | |
| 73 73 | 
             
                assert(!xml.xpath('/fb/f/f').empty?)
         | 
| 74 74 | 
             
                assert(!xml.xpath('/fb/f/class').empty?)
         | 
| 75 75 | 
             
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              def test_sorts_keys
         | 
| 78 | 
            +
                fb = Factbase.new
         | 
| 79 | 
            +
                f = fb.insert
         | 
| 80 | 
            +
                f.x = 20
         | 
| 81 | 
            +
                f.t = 40
         | 
| 82 | 
            +
                f.a = 10
         | 
| 83 | 
            +
                f.c = 1
         | 
| 84 | 
            +
                xml = Factbase::ToXML.new(fb).xml
         | 
| 85 | 
            +
                assert(xml.gsub(/\s*/, '').include?('<f><a>10</a><c>1</c><t>40</t><x>20</x></f>'), xml)
         | 
| 86 | 
            +
              end
         | 
| 76 87 | 
             
            end
         | 
| @@ -42,4 +42,14 @@ class TestToYAML < Minitest::Test | |
| 42 42 | 
             
                assert_equal(42, yaml['facts'][0]['foo'][0])
         | 
| 43 43 | 
             
                assert_equal(256, yaml['facts'][0]['foo'][1])
         | 
| 44 44 | 
             
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def test_sorts_keys
         | 
| 47 | 
            +
                fb = Factbase.new
         | 
| 48 | 
            +
                f = fb.insert
         | 
| 49 | 
            +
                f.b = 42
         | 
| 50 | 
            +
                f.a = 256
         | 
| 51 | 
            +
                f.c = 10
         | 
| 52 | 
            +
                yaml = Factbase::ToYAML.new(fb).yaml
         | 
| 53 | 
            +
                assert(yaml.include?("a: 256\n  b: 42\n  c: 10"), yaml)
         | 
| 54 | 
            +
              end
         | 
| 45 55 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: factbase
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.42
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Yegor Bugayenko
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024-06- | 
| 11 | 
            +
            date: 2024-06-04 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: json
         |