Skip to content

Commit

Permalink
feat(core): track method_missing commit triggers
Browse files Browse the repository at this point in the history
  • Loading branch information
marian13 committed Mar 21, 2023
1 parent 7ba27df commit f3a7264
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 13 deletions.
9 changes: 2 additions & 7 deletions lib/convenient_service/core/entities/config.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require_relative "config/commands"
require_relative "config/entities"
require_relative "config/errors"

Expand Down Expand Up @@ -130,7 +131,7 @@ def committed?
#
def commit!(trigger: Constants::Triggers::USER)
concerns.include!
.tap { track_commit_trigger!(trigger) }
.tap { Commands::TrackMethodMissingCommitTrigger.call(config: self, trigger: trigger) }
end

private
Expand All @@ -150,12 +151,6 @@ def assert_not_committed!

raise Errors::ConfigIsCommitted.new(config: self)
end

##
# @return [void]
#
def track_commit_trigger!(trigger)
end
end
end
end
Expand Down
3 changes: 3 additions & 0 deletions lib/convenient_service/core/entities/config/commands.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# frozen_string_literal: true

require_relative "commands/track_method_missing_commit_trigger"
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# frozen_string_literal: true

module ConvenientService
module Core
module Entities
class Config
module Commands
class TrackMethodMissingCommitTrigger < Support::Command
##
# @!attribute [r] config
# @return [ConvenientService::Core::Entities::Config]
#
attr_reader :config

##
# @!attribute [r] trigger
# @return [ConvenientService::Support::UniqueValue]
#
attr_reader :trigger

##
# @param config [ConvenientService::Core::Entities::Config]
# @param trigger [ConvenientService::Support::UniqueValue]
# @return [void]
#
def initialize(config:, trigger:)
@config = config
@trigger = trigger
end

##
# @return [void]
#
def call
return unless method_missing_trigger_valid?
return if method_missing_commits_counter_incremented?

raise Errors::TooManyCommitsFromMethodMissing.new(config: config)
end

##
# @return [Boolean]
#
def method_missing_trigger_valid?
method_missing_triggers.any?(trigger)
end

##
# @return [Boolean]
#
def method_missing_commits_counter_incremented?
Utils::Object.memoize_including_falsy_values(self, :@method_missing_commits_counter_incremented) { config.method_missing_commits_counter.bincrement }
end

##
# @return [Array<ConvenientService::Support::UniqueValue>]
#
def method_missing_triggers
@method_missing_triggers ||= [
Constants::Triggers::INSTANCE_METHOD_MISSING,
Constants::Triggers::CLASS_METHOD_MISSING
]
end
end
end
end
end
end
end
37 changes: 37 additions & 0 deletions lib/convenient_service/core/entities/config/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,43 @@ def initialize(config:)
super(message)
end
end

class TooManyCommitsFromMethodMissing < ::ConvenientService::Error
##
# @param config [ConvenientService::Core::Entities::Config]
# @return [void]
#
# @internal
# TODO: Create a troubleshooting page with possible reasons (preliminary RSpec mocks etc).
# Append a link to it to the error message.
#
def initialize(config:)
message = <<~TEXT
`#{config.klass}` config is committed too many times from `method_missing`.
In order to resolve this issue try to commit it manually before usage of any config-dependent method.
Example 1 (outside class):
# Commitment:
#{config.klass}.commit_config!
# Few lines later - usage:
#{config.klass}.result # or whatever method.
Example 2 (inside class):
class #{config.klass}
# ...
commit_config!
step :result # or any other method that becomes available after config commitment.
end
TEXT

super(message)
end
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# frozen_string_literal: true

require "spec_helper"

require "convenient_service"

# rubocop:disable RSpec/NestedGroups
RSpec.describe ConvenientService::Core::Entities::Config::Commands::TrackMethodMissingCommitTrigger do
example_group "class methods" do
subject(:command_result) { described_class.call(config: config, trigger: trigger) }

let(:config) { ConvenientService::Core::Entities::Config.new(klass: service_class) }

let(:service_class) do
Class.new do
include ConvenientService::Core
end
end

describe ".call" do
context "when `method_missing` trigger is NOT valid" do
let(:trigger) { ConvenientService::Core::Constants::Triggers::USER }

it "does NOT raise `ConvenientService::Core::Entities::Config::Errors::TooManyCommitsFromMethodMissing`" do
expect { command_result }.not_to raise_error
end

it "does NOT increment `method_missing` commit counter" do
expect { command_result }.not_to change { config.method_missing_commits_counter.current_value }
end
end

context "when `method_missing` trigger is valid" do
context "when `method_missing` trigger is `ConvenientService::Core::Constants::Triggers::INSTANCE_METHOD_MISSING`" do
let(:trigger) { ConvenientService::Core::Constants::Triggers::INSTANCE_METHOD_MISSING }

before do
config.method_missing_commits_counter.current_value = config.method_missing_commits_counter.max_value
end

context "when `method_missing` counter is NOT incremented" do
let(:error_message) do
<<~TEXT
`#{config.klass}` config is committed too many times from `method_missing`.
In order to resolve this issue try to commit it manually before usage of any config-dependent method.
Example 1 (outside class):
# Commitment:
#{config.klass}.commit_config!
# Few lines later - usage:
#{config.klass}.result # or whatever method.
Example 2 (inside class):
class #{config.klass}
# ...
commit_config!
step :result # or any other method that becomes available after config commitment.
end
TEXT
end

it "raises `ConvenientService::Core::Entities::Config::Errors::TooManyCommitsFromMethodMissing`" do
expect { command_result }
.to raise_error(ConvenientService::Core::Entities::Config::Errors::TooManyCommitsFromMethodMissing)
.with_message(error_message)
end
end

context "when `method_missing` counter is incremented" do
before do
config.method_missing_commits_counter.reset
end

it "does NOT raise `ConvenientService::Core::Entities::Config::Errors::TooManyCommitsFromMethodMissing`" do
expect { command_result }.not_to raise_error
end

it "increments `method_missing` commit counter" do
expect { command_result }.to change(config.method_missing_commits_counter, :current_value).from(config.method_missing_commits_counter.current_value).to(config.method_missing_commits_counter.current_value + 1)
end
end
end

context "when `method_missing` trigger is `ConvenientService::Core::Constants::Triggers::CLASS_METHOD_MISSING`" do
let(:trigger) { ConvenientService::Core::Constants::Triggers::CLASS_METHOD_MISSING }

before do
config.method_missing_commits_counter.current_value = config.method_missing_commits_counter.max_value
end

context "when `method_missing` counter is NOT incremented" do
let(:error_message) do
<<~TEXT
`#{config.klass}` config is committed too many times from `method_missing`.
In order to resolve this issue try to commit it manually before usage of any config-dependent method.
Example 1 (outside class):
# Commitment:
#{config.klass}.commit_config!
# Few lines later - usage:
#{config.klass}.result # or whatever method.
Example 2 (inside class):
class #{config.klass}
# ...
commit_config!
step :result # or any other method that becomes available after config commitment.
end
TEXT
end

it "raises `ConvenientService::Core::Entities::Config::Errors::TooManyCommitsFromMethodMissing`" do
expect { command_result }
.to raise_error(ConvenientService::Core::Entities::Config::Errors::TooManyCommitsFromMethodMissing)
.with_message(error_message)
end
end

context "when `method_missing` counter is incremented" do
before do
config.method_missing_commits_counter.reset
end

it "does NOT raise `ConvenientService::Core::Entities::Config::Errors::TooManyCommitsFromMethodMissing`" do
expect { command_result }.not_to raise_error
end

it "increments `method_missing` commit counter" do
expect { command_result }.to change(config.method_missing_commits_counter, :current_value).from(config.method_missing_commits_counter.current_value).to(config.method_missing_commits_counter.current_value + 1)
end
end
end
end
end
end
end
# rubocop:enable RSpec/NestedGroups
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
include ConvenientService::RSpec::Matchers::BeDescendantOf

specify { expect(described_class::ConfigIsCommitted).to be_descendant_of(ConvenientService::Error) }
specify { expect(described_class::TooManyCommitsFromMethodMissing).to be_descendant_of(ConvenientService::Error) }
end
12 changes: 6 additions & 6 deletions spec/lib/convenient_service/core/entities/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -428,24 +428,24 @@ def next(...)

specify do
expect { config.commit! }
.to delegate_to(config, :track_commit_trigger!)
.with_arguments(ConvenientService::Core::Constants::Triggers::USER)
.to delegate_to(ConvenientService::Core::Entities::Config::Commands::TrackMethodMissingCommitTrigger, :call)
.with_arguments(config: config, trigger: ConvenientService::Core::Constants::Triggers::USER)
end

example_group "`trigger` option" do
context "when `trigger` is NOT passed" do
it "defaults `ConvenientService::Core::Constants::Triggers::USER`" do
expect { config.commit! }
.to delegate_to(config, :track_commit_trigger!)
.with_arguments(ConvenientService::Core::Constants::Triggers::USER)
.to delegate_to(ConvenientService::Core::Entities::Config::Commands::TrackMethodMissingCommitTrigger, :call)
.with_arguments(config: config, trigger: ConvenientService::Core::Constants::Triggers::USER)
end
end

context "when `trigger` is passed" do
specify do
expect { config.commit!(trigger: ConvenientService::Core::Constants::Triggers::INSTANCE_METHOD_MISSING) }
.to delegate_to(config, :track_commit_trigger!)
.with_arguments(ConvenientService::Core::Constants::Triggers::INSTANCE_METHOD_MISSING)
.to delegate_to(ConvenientService::Core::Entities::Config::Commands::TrackMethodMissingCommitTrigger, :call)
.with_arguments(config: config, trigger: ConvenientService::Core::Constants::Triggers::INSTANCE_METHOD_MISSING)
end
end
end
Expand Down

0 comments on commit f3a7264

Please sign in to comment.