--
-- Inline class and module definitions.  Bodies run inside a block, where
-- `class`/`module` are legal; definitions land at top level, so they are
-- shared session-wide (like plruby_modules) and persist across calls.
--
CREATE FUNCTION cls_define_and_use(int, int) RETURNS text LANGUAGE plruby AS $$
    class PLRubyTestVec
        attr_reader :x, :y
        def initialize(x, y) = (@x, @y = x, y)
        def +(o) = PLRubyTestVec.new(@x + o.x, @y + o.y)
        def to_s = "(#{@x},#{@y})"
    end
    (PLRubyTestVec.new(args[0], args[1]) + PLRubyTestVec.new(10, 20)).to_s
$$;
SELECT cls_define_and_use(1, 2);

-- The class defined above is visible from a different function.
CREATE FUNCTION cls_reuse() RETURNS text LANGUAGE plruby AS $$
    PLRubyTestVec.new(7, 8).to_s
$$;
SELECT cls_reuse();

-- Modules and constants work too, and reopening a class is fine.
CREATE FUNCTION cls_module() RETURNS text LANGUAGE plruby AS $$
    module PLRubyTestUtil
        LIMIT = 40
        def self.clamp(n) = [n, LIMIT].min
    end
    class PLRubyTestVec
        def manhattan = x.abs + y.abs
    end
    "clamped=#{PLRubyTestUtil.clamp(99)} dist=#{PLRubyTestVec.new(-3, 4).manhattan}"
$$;
SELECT cls_module();

-- Struct/Comparable-style one-liners inside a DO block.
DO $$
    class PLRubyTestPoint < Struct.new(:x, :y)
        include Comparable
        def <=>(o) = (x * x + y * y) <=> (o.x * o.x + o.y * o.y)
    end
    pts = [PLRubyTestPoint.new(3, 4), PLRubyTestPoint.new(1, 1)]
    elog('NOTICE', "nearest=#{pts.min.to_a.inspect}")
$$ LANGUAGE plruby;

-- Classes work in trigger bodies as well.
CREATE TABLE cls_t (v text);
CREATE FUNCTION cls_trig() RETURNS trigger LANGUAGE plruby AS $$
    class PLRubyTestTag
        def self.tag(s) = "[#{s}]"
    end
    $_TD['new']['v'] = PLRubyTestTag.tag($_TD['new']['v'])
    'MODIFY'
$$;
CREATE TRIGGER cls_t_trig BEFORE INSERT ON cls_t
    FOR EACH ROW EXECUTE PROCEDURE cls_trig();
INSERT INTO cls_t VALUES ('hello');
SELECT * FROM cls_t;

DROP TABLE cls_t;
