From 88973ca5740644d0f1e15e711ecad4b2b4d5283c Mon Sep 17 00:00:00 2001 From: Alteras1 <42795314+Alteras1@users.noreply.github.com.> Date: Sat, 26 Apr 2025 11:24:29 -0700 Subject: [PATCH] Add Roll API for development `/rollmaster/roll.json?diceRolls[]=1d20&diceRolls[]=` This is not intended to be used as part of the post rolling process, but instead is a quick access hatch to reach the library code without going through post creation. Further endpoints may be added for functionality. --- .../rollmaster/examples_controller.rb | 11 ----- app/controllers/rollmaster/roll_controller.rb | 20 ++++++++++ config/routes.rb | 2 +- lib/rollmaster/dice_engine.rb | 20 ++++++++-- lib/rollmaster/handle_cooked_post_process.rb | 6 +++ plugin.rb | 4 +- scripts/debug.js | 12 +++++- spec/lib/rollmaster/dice_engine_spec.rb | 20 +++++++++- .../rollmaster/roll_controller_spec.rb | 40 +++++++++++++++++++ 9 files changed, 115 insertions(+), 20 deletions(-) delete mode 100644 app/controllers/rollmaster/examples_controller.rb create mode 100644 app/controllers/rollmaster/roll_controller.rb create mode 100644 spec/requests/rollmaster/roll_controller_spec.rb diff --git a/app/controllers/rollmaster/examples_controller.rb b/app/controllers/rollmaster/examples_controller.rb deleted file mode 100644 index fa406bd..0000000 --- a/app/controllers/rollmaster/examples_controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module ::Rollmaster - class ExamplesController < ::ApplicationController - requires_plugin PLUGIN_NAME - - def index - render json: { hello: "world" } - end - end -end diff --git a/app/controllers/rollmaster/roll_controller.rb b/app/controllers/rollmaster/roll_controller.rb new file mode 100644 index 0000000..b17d5ac --- /dev/null +++ b/app/controllers/rollmaster/roll_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module ::Rollmaster + class RollController < ::ApplicationController + requires_plugin PLUGIN_NAME + + # GET /rollmaster/roll + # @param [Array] diceRolls array of dice rolls to be + # @return [Hash] results array of dice rolls + # @example URL /rollmaster/roll?diceRolls[]=2d6&diceRolls[]=1d20 + def roll + begin + result = ::Rollmaster::DiceEngine.roll(*params[:diceRolls]) + render json: { result: result } + rescue ::Rollmaster::DiceEngine::RollError => e + raise Discourse::InvalidParameters, e.message + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 028399c..a8e2b60 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true Rollmaster::Engine.routes.draw do - get "/examples" => "examples#index" + get "/roll" => "roll#roll" # define routes here end diff --git a/lib/rollmaster/dice_engine.rb b/lib/rollmaster/dice_engine.rb index 99c7f29..882497e 100644 --- a/lib/rollmaster/dice_engine.rb +++ b/lib/rollmaster/dice_engine.rb @@ -2,8 +2,11 @@ require "mini_racer" -module Rollmaster +module ::Rollmaster class DiceEngine + class RollError < StandardError + end + @mutex = Mutex.new @ctx_init = Mutex.new @ctx = nil @@ -20,6 +23,9 @@ def self.roll(*diceRolls) context = v8 result = context.call("roll", *diceRolls) end + if result.is_a?(Hash) && result["type"] == "error" + raise Rollmaster::DiceEngine::RollError.new(result["msg"]) + end result end @@ -27,8 +33,16 @@ def self.attach_function(ctx) ctx.eval <<~JS function roll(...diceRolls) { const roller = new rpgDiceRoller.DiceRoller; - roller.roll(...diceRolls); - return JSON.parse(JSON.stringify(roller.log)) + try { + roller.roll(...diceRolls); + return JSON.parse(JSON.stringify(roller.log)) + } catch (e) { + return { + type: "error", + name: e.name, + msg: e.message, + }; + } } JS end diff --git a/lib/rollmaster/handle_cooked_post_process.rb b/lib/rollmaster/handle_cooked_post_process.rb index 438e069..b3ea412 100644 --- a/lib/rollmaster/handle_cooked_post_process.rb +++ b/lib/rollmaster/handle_cooked_post_process.rb @@ -4,6 +4,12 @@ module ::Rollmaster class HandleCookedPostProcess def self.process(doc, post) # Add your processing logic here + + # parse the post content + + # check for existing dice rolls associated with post + + # create new dice rolls for any new ones end end end diff --git a/plugin.rb b/plugin.rb index 5267e59..9a5e274 100644 --- a/plugin.rb +++ b/plugin.rb @@ -18,8 +18,8 @@ module ::Rollmaster after_initialize do # Code which should run after Rails has finished booting - # # I don't think this is needed, but it doesn't hurt to be safe - # PrettyText.reset_context() + # I don't think this is needed, but it doesn't hurt to be safe + ::Rollmaster::DiceEngine.reset_context on(:post_process_cooked) { |doc, post| ::Rollmaster::HandleCookedPostProcess.process(doc, post) } # TODO: consider :chat_message_processed as well diff --git a/scripts/debug.js b/scripts/debug.js index e6ce411..fc18e73 100644 --- a/scripts/debug.js +++ b/scripts/debug.js @@ -11,8 +11,16 @@ const { DiceRoller } = require("@dice-roller/rpg-dice-roller"); function roll(...diceRolls) { const roller = new DiceRoller(); - roller.roll(...diceRolls); - return roller.log; + try { + roller.roll(...diceRolls); + return roller.log; + } catch (e) { + return { + type: "error", + name: e.name, + msg: e.message, + }; + } } const r = repl.start({ prompt: "> " }); diff --git a/spec/lib/rollmaster/dice_engine_spec.rb b/spec/lib/rollmaster/dice_engine_spec.rb index ef1c79c..2308e82 100644 --- a/spec/lib/rollmaster/dice_engine_spec.rb +++ b/spec/lib/rollmaster/dice_engine_spec.rb @@ -13,6 +13,24 @@ expect(result.size).to eq(dice_rolls.size) expect(result.all? { |r| r.is_a?(Hash) }).to be(true) end + + it "raises a RollError for invalid dice rolls" do + dice_rolls = ["invalid_roll"] + + expect { described_class.roll(*dice_rolls) }.to raise_error(Rollmaster::DiceEngine::RollError) + end + + it "raises a RollError for empty dice rolls" do + dice_rolls = [] + + expect { described_class.roll(*dice_rolls) }.to raise_error(Rollmaster::DiceEngine::RollError) + end + + it "raises a RollError for nil dice rolls" do + dice_rolls = nil + + expect { described_class.roll(*dice_rolls) }.to raise_error(Rollmaster::DiceEngine::RollError) + end end describe ".reset_context" do @@ -60,7 +78,7 @@ end end - xdescribe ".attach_function" do + describe ".attach_function" do it "attaches the roll function to the context" do context = MiniRacer::Context.new described_class.attach_function(context) diff --git a/spec/requests/rollmaster/roll_controller_spec.rb b/spec/requests/rollmaster/roll_controller_spec.rb new file mode 100644 index 0000000..fc77e25 --- /dev/null +++ b/spec/requests/rollmaster/roll_controller_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +RSpec.describe Rollmaster::RollController, type: :request do + before { SiteSetting.rollmaster_enabled = true } + + describe "GET /rollmaster/roll" do + let(:endpoint) { "/rollmaster/roll.json" } + + context "when valid dice rolls are provided" do + it "returns the correct results" do + get "/rollmaster/roll.json", params: { diceRolls: %w[2d6 1d20] } + expect(response).to have_http_status(:ok) + json = response.parsed_body + expect(json["result"]).to be_an(Array) + expect(json["result"].size).to eq(2) + end + end + + context "when no dice rolls are provided" do + it "raises an invalid parameters error" do + get "/rollmaster/roll.json" + expect(response.status).to eq(400) + end + end + + context "when an invalid dice roll is provided" do + before do + allow(::Rollmaster::DiceEngine).to receive(:roll).and_raise( + ::Rollmaster::DiceEngine::RollError, + "Invalid dice roll", + ) + end + + it "raises an invalid parameters error" do + get "/rollmaster/roll.json", params: { diceRolls: ["invalid"] } + expect(response.status).to eq(400) + end + end + end +end