Skip to content

Commit

Permalink
Test HookPoint class methods
Browse files Browse the repository at this point in the history
  • Loading branch information
TonyCTHsu committed May 13, 2024
1 parent 4b47e13 commit 96b7443
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 0 deletions.
28 changes: 28 additions & 0 deletions lib/graft/hook_point.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ class HookPoint
DEFAULT_STRATEGY = Module.respond_to?(:prepend) ? :prepend : :chain

class << self
# Parses a hook point string into its components.
#
# @param hook_point [String] The hook point string to parse.
# @return [Array<Symbol, Symbol, Symbol>] An array containing the parsed components:
# - klass_name [Symbol]: The name of the class or module.
# - method_kind [Symbol]: The kind of method, either :klass_method or :instance_method.
# - method_name [Symbol]: The name of the method.
# @raise [ArgumentError] If the hook point string is invalid or missing any components.
def parse(hook_point)
klass_name, separator, method_name = hook_point.split(/(\#|\.)/, 2)

Expand All @@ -32,26 +40,46 @@ def parse(hook_point)
[klass_name.to_sym, method_kind, method_name.to_sym]
end

# Checks if a constant with the given name exists.
#
# @param name [String] the name of the constant to check
# @return [Boolean] true if the constant exists, false otherwise
def const_exist?(name)
resolve_const(name) && true
rescue NameError, ArgumentError
false
end

# Resolves a constant by its name.
#
# @param name [String] The name of the constant to resolve.
# @return [Object] The resolved constant.
# @raise [ArgumentError] If the name is nil or empty, or if the constant is not found.
def resolve_const(name)
raise ArgumentError, "const not found: #{name}" if name.nil? || name.empty?

name.to_s.split("::").inject(Object) { |a, e| a.const_get(e, false) }
end

# Resolves a module by name.
#
# @param name [String] The name of the module to resolve.
# @return [Module] The resolved module.
# @raise [ArgumentError] If the resolved constant is not a Module.
def resolve_module(name)
const = resolve_const(name)

# TODO: Not covered by tests, cannot find a const that is not a Module
raise ArgumentError, "not a Module: #{name}" unless const.is_a?(Module)

const
end

# Returns the strategy module based on the given strategy symbol.
#
# @param strategy [Symbol] The strategy symbol (:prepend or :chain).
# @return [Module] The strategy module corresponding to the given strategy symbol.
# @raise [HookPointError] If the strategy symbol is unknown.
def strategy_module(strategy)
case strategy
when :prepend then Prepend
Expand Down
121 changes: 121 additions & 0 deletions spec/graft/hook_point_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
require "graft/hook_point"

RSpec.describe Graft::HookPoint do
describe ".parse" do
context "when given invalid string" do
[
# Add more invalid strings here...
"",
"Dummy"
].each do |str|
it { expect { described_class.parse(str) }.to raise_error(ArgumentError) }
end

[
"Dummy#",
"Dummy."
].each do |str|
it do
pending "FIXME: This is not working as expected"
expect { described_class.parse(str) }.to raise_error(ArgumentError)
end
end
end

context "when an valid instance method notation" do
it do
klass_name, method_kind, method_name = described_class.parse("Dummy#foo")

expect(klass_name).to eq :Dummy
expect(method_kind).to eq :instance_method
expect(method_name).to eq :foo
end
end

context "when an valid class method notation" do
it do
klass_name, method_kind, method_name = described_class.parse("Dummy.foo")

expect(klass_name).to eq :Dummy
expect(method_kind).to eq :klass_method
expect(method_name).to eq :foo
end
end
end

describe ".const_exist?" do
context "when given a string of existing Constant" do
it { expect(described_class.const_exist?(described_class.to_s)).to be true }
end

context "when given an invalid input" do
[
nil,
"",
true,
:symbol
].each do |input|
it { expect(described_class.const_exist?(input)).to be false }
end
end
end

describe ".resolve_const" do
context "when given a string of existing Constant" do
it { expect(described_class.resolve_const("Object")).to eql Object }
it { expect(described_class.resolve_const("Module")).to eql Module }
it { expect(described_class.resolve_const("Graft::HookPoint")).to eql Graft::HookPoint }

it do
pending "FIXME: This is not working as expected"
expect(described_class.resolve_const("::Graft::HookPoint")).to eql Graft::HookPoint
end
end

context "when given an invalid input" do
it { expect { described_class.resolve_const(nil) }.to raise_error(ArgumentError, /const not found/) }
it { expect { described_class.resolve_const("") }.to raise_error(ArgumentError, /const not found/) }
it { expect { described_class.resolve_const("Foo::Bar::Baz") }.to raise_error(NameError) }
it { expect { described_class.resolve_const("::Foo::Bar::Baz") }.to raise_error(NameError) }
end
end

describe ".resolve_module" do
context "when given a string of existing Constant" do
it { expect(described_class.resolve_module("Object")).to eql Object }
it { expect(described_class.resolve_module("Module")).to eql Module }
it { expect(described_class.resolve_module("Graft::HookPoint")).to eql Graft::HookPoint }

it do
pending "FIXME: This is not working as expected"
expect(described_class.resolve_module("::Graft::HookPoint")).to eql Graft::HookPoint
end
end

context "when given an invalid input" do
it { expect { described_class.resolve_module(nil) }.to raise_error(ArgumentError, /const not found/) }
it { expect { described_class.resolve_module("") }.to raise_error(ArgumentError, /const not found/) }
it { expect { described_class.resolve_module("Foo::Bar::Baz") }.to raise_error(NameError) }
it { expect { described_class.resolve_module("::Foo::Bar::Baz") }.to raise_error(NameError) }

it do
stub_const("MyClass", "")
expect { described_class.resolve_module("MyClass") }.to raise_error(ArgumentError, /not a Module/)
end
end
end

describe ".strategy_module" do
context "when given :prepend" do
it { expect(described_class.strategy_module(:prepend)).to eql Graft::HookPoint::Prepend }
end

context "when given :chain" do
it { expect(described_class.strategy_module(:chain)).to eql Graft::HookPoint::Chain }
end

context "when given invalid" do
it { expect { described_class.strategy_module(:dummy) }.to raise_error(Graft::HookPointError, /unknown strategy/) }
end
end
end

0 comments on commit 96b7443

Please sign in to comment.