From ae2009f996cfa0f59e35794fdc0366090d2d3346 Mon Sep 17 00:00:00 2001 From: "Marcos G. Zimmermann" Date: Mon, 3 Feb 2025 11:15:28 -0300 Subject: [PATCH] chore: refactoring primitive string --- lib/site_maps/primitive/string.rb | 85 ++++++++-- spec/site_maps/primitive/string_spec.rb | 213 ++++++++++++++++++++---- 2 files changed, 248 insertions(+), 50 deletions(-) diff --git a/lib/site_maps/primitive/string.rb b/lib/site_maps/primitive/string.rb index 335a54e..d5dd50f 100644 --- a/lib/site_maps/primitive/string.rb +++ b/lib/site_maps/primitive/string.rb @@ -14,32 +14,30 @@ module SiteMaps::Primitive class String < ::String - def classify - new_str = if defined?(Dry::Inflector) - Dry::Inflector.new.classify(self) - elsif defined?(ActiveSupport::Inflector) - ActiveSupport::Inflector.classify(self) - else - split("/").collect do |c| - c.split("_").collect(&:capitalize).join - end.join("::") + def self.inflector + return @inflector if defined?(@inflector) + + @inflector = if defined?(::ActiveSupport::Inflector) + ::ActiveSupport::Inflector + elsif defined?(::Dry::Inflector) + ::Dry::Inflector.new end + end + + def classify + new_str = inflector&.classify(self) || split("/").collect do |c| + c.split("_").collect(&:capitalize).join + end.join("::") self.class.new(new_str) end def constantize - if defined?(Dry::Inflector) - Dry::Inflector.new.constantize(self) - elsif defined?(ActiveSupport::Inflector) - ActiveSupport::Inflector.constantize(self) - else - Object.const_get(self) - end + inflector&.constantize(self) || Object.const_get(self) end def underscore - new_str = sub(/^::/, "") + new_str = inflector&.underscore(self) || sub(/^::/, "") .gsub("::", "/") .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') .gsub(/([a-z\d])([A-Z])/, '\1_\2') @@ -51,5 +49,58 @@ def underscore self.class.new(new_str) end + + def pluralize + new_str = inflector&.pluralize(self) || begin + # dummy pluralize + if /y$/.match?(self) + sub(/y$/, "ies") + elsif /s$/.match?(self) + self + else + self + "s" + end + end + + new_str.is_a?(self.class) ? new_str : self.class.new(new_str) + end + + def singularize + new_str = inflector&.singularize(self) || begin + # dummy singularize + if /ies$/.match?(self) + sub(/ies$/, "y") + elsif /s$/.match?(self) + sub(/s$/, "") + else + self + end + end + + new_str.is_a?(self.class) ? new_str : self.class.new(new_str) + end + + def camelize(uppercase_first_letter = true) + new_str = inflector&.camelize(self, uppercase_first_letter) || begin + # dummy camelize + str = self.to_s + str = str.sub(/^[a-z\d]*/) { $&.capitalize } + str = str.tr("-", "_") + str = str.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" } + str = str.gsub("/", "::") + unless uppercase_first_letter + str = str.sub(/^[A-Z]*/) { $&.downcase } + end + str + end + + self.class.new(new_str) + end + + protected + + def inflector + self.class.inflector + end end end diff --git a/spec/site_maps/primitive/string_spec.rb b/spec/site_maps/primitive/string_spec.rb index ba43221..c49ba93 100644 --- a/spec/site_maps/primitive/string_spec.rb +++ b/spec/site_maps/primitive/string_spec.rb @@ -3,32 +3,60 @@ require "spec_helper" RSpec.describe SiteMaps::Primitive::String do - describe "#classify" do - context "when dry-inflector is available" do + after do + described_class.remove_instance_variable(:@inflector) + end + + describe ".inflector" do + subject { described_class.inflector } + + before do + described_class.remove_instance_variable(:@inflector) + rescue NameError + end + + context "when ActiveSupport::Inflector is available" do before do - stub_const("Dry::Inflector", Class.new { - def classify(string) - "Classified" - end - }) + stub_const("ActiveSupport::Inflector", Class.new) end - it "returns the classified string" do - expect(described_class.new("string").classify).to eq("Classified") - end + it { is_expected.to eq(ActiveSupport::Inflector) } end - context "when active-support is available" do + context "when Dry::Inflector is available" do before do - stub_const("ActiveSupport::Inflector", Class.new { - def self.classify(string) - "Classified" - end - }) + stub_const("Dry::Inflector", Class.new) + end + + it { is_expected.to be_a(Dry::Inflector) } + end + + context "when no inflector is available" do + it { is_expected.to be_nil } + end + end + + describe "#classify" do + let(:inflector) { nil } + + before do + described_class.instance_variable_set(:@inflector, inflector) + end + + context "when inflector is available" do + let(:inflector) do + Class.new { + def classify(string) + "Classified" + end + }.new end it "returns the classified string" do - expect(described_class.new("string").classify).to eq("Classified") + allow(inflector).to receive(:classify) + expect(described_class.new("classified").classify).to eq("Classified") + + expect(inflector).to have_received(:classify).with("classified") end end @@ -41,18 +69,20 @@ def self.classify(string) describe "#constantize" do let(:constant) { Class.new } + let(:inflector) { nil } before do + described_class.instance_variable_set(:@inflector, inflector) stub_const("MyConstant", constant) end - context "when dry-inflector is available" do - before do - stub_const("Dry::Inflector", Class.new { - def constantize(string) - MyConstant if string == "MyConstant" - end - }) + context "when inflector is available" do + let(:inflector) do + Class.new { + def constantize(string) + MyConstant if string == "MyConstant" + end + }.new end it "returns the constantized string" do @@ -60,15 +90,7 @@ def constantize(string) end end - context "when active-support is available" do - before do - stub_const("ActiveSupport::Inflector", Class.new { - def self.constantize(string) - MyConstant if string == "MyConstant" - end - }) - end - + context "when no inflector is available" do it "returns the constantized string" do expect(described_class.new("MyConstant").constantize).to eq(constant) end @@ -126,4 +148,129 @@ def self.constantize(string) it { is_expected.to eq("user_name") } end end + + describe "#pluralize" do + let(:inflector) { nil } + + before do + described_class.instance_variable_set(:@inflector, inflector) + end + + context "when inflector is available" do + let(:inflector) do + Class.new { + def pluralize(string) + "users" + end + }.new + end + + it "returns the pluralized string" do + allow(inflector).to receive(:pluralize) + expect(described_class.new("user").pluralize).to eq("users") + + expect(inflector).to have_received(:pluralize).with("user") + end + end + + context "when no inflector is available" do + it "returns the pluralized string" do + expect(described_class.new("user").pluralize).to eq("users") + end + + it "returns pluralized string ending with 'y'" do + expect(described_class.new("city").pluralize).to eq("cities") + end + end + end + + describe "#singularize" do + let(:inflector) { nil } + + before do + described_class.instance_variable_set(:@inflector, inflector) + end + + context "when inflector is available" do + let(:inflector) do + Class.new { + def singularize(string) + "user" + end + }.new + end + + it "returns the singularized string" do + allow(inflector).to receive(:singularize) + expect(described_class.new("users").singularize).to eq("user") + + expect(inflector).to have_received(:singularize).with("users") + end + end + + context "when no inflector is available" do + it "returns the singularized string" do + expect(described_class.new("users").singularize).to eq("user") + end + + it "returns singularized string ending with 'ies'" do + expect(described_class.new("cities").singularize).to eq("city") + end + end + end + + describe "#camelize" do + let(:inflector) { nil } + + before do + described_class.instance_variable_set(:@inflector, inflector) + end + + context "when inflector is available" do + let(:inflector) do + Class.new { + def camelize(string, uppercase_first_letter) + "User" + end + }.new + end + + it "returns the camelized string" do + allow(inflector).to receive(:camelize) + expect(described_class.new("user").camelize).to eq("User") + + expect(inflector).to have_received(:camelize).with("user", true) + end + end + + context "when no inflector is available" do + it "returns the camelized string" do + expect(described_class.new("user").camelize).to eq("User") + end + + it "returns the camelized string with uppercase first letter" do + expect(described_class.new("user").camelize(true)).to eq("User") + end + + it "returns the camelized string with lowercase first letter" do + expect(described_class.new("user").camelize(false)).to eq("user") + end + + it "returns the camelized string with uppercase first letter and underscore" do + expect(described_class.new("user_name").camelize(true)).to eq("UserName") + end + + it "returns the camelized string with lowercase first letter and underscore" do + expect(described_class.new("user_name").camelize(false)).to eq("userName") + end + + it "returns the camelized string with uppercase first letter and dash" do + expect(described_class.new("user-name").camelize(true)).to eq("UserName") + end + + it "returns the camelized string with namespace" do + expect(described_class.new("admin/user_name").camelize(true)).to eq("Admin::UserName") + end + end + end end