From f6c92240b486b7fc1f2782895dff30a059eb28af Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Fri, 15 Sep 2023 01:00:18 +0100 Subject: [PATCH 01/17] Create tokeniser --- lib/kangaru/inflectors/tokeniser.rb | 20 +++ sig/kangaru/inflectors/tokeniser.rbs | 14 ++ spec/kangaru/inflectors/tokeniser_spec.rb | 154 ++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 lib/kangaru/inflectors/tokeniser.rb create mode 100644 sig/kangaru/inflectors/tokeniser.rbs create mode 100644 spec/kangaru/inflectors/tokeniser_spec.rb diff --git a/lib/kangaru/inflectors/tokeniser.rb b/lib/kangaru/inflectors/tokeniser.rb new file mode 100644 index 0000000..f347beb --- /dev/null +++ b/lib/kangaru/inflectors/tokeniser.rb @@ -0,0 +1,20 @@ +module Kangaru + module Inflectors + class Tokeniser + GROUP_DELIMITER = %r{/|::} + TOKEN_DELIMITER = /[_-]+|(?=[A-Z][a-z])/ + + attr_reader :string + + def initialize(string) + @string = string + end + + def split + string.split(GROUP_DELIMITER).map do |group| + group.split(TOKEN_DELIMITER) + end + end + end + end +end diff --git a/sig/kangaru/inflectors/tokeniser.rbs b/sig/kangaru/inflectors/tokeniser.rbs new file mode 100644 index 0000000..2acc142 --- /dev/null +++ b/sig/kangaru/inflectors/tokeniser.rbs @@ -0,0 +1,14 @@ +module Kangaru + module Inflectors + class Tokeniser + GROUP_DELIMITER: Regexp + TOKEN_DELIMITER: Regexp + + attr_reader string: String + + def initialize: (String) -> void + + def split: -> Array[Array[String]] + end + end +end diff --git a/spec/kangaru/inflectors/tokeniser_spec.rb b/spec/kangaru/inflectors/tokeniser_spec.rb new file mode 100644 index 0000000..0fbee4d --- /dev/null +++ b/spec/kangaru/inflectors/tokeniser_spec.rb @@ -0,0 +1,154 @@ +RSpec.describe Kangaru::Inflectors::Tokeniser do + subject(:tokeniser) { described_class.new(string) } + + describe "#split" do + subject(:tokens) { tokeniser.split } + + shared_examples :tokenises_string do |options| + let(:expected_tokens) { options[:to] } + + it "returns the expected tokens" do + expect(tokens).to eq(expected_tokens) + end + end + + describe "splitting groups" do + context "when string has one word" do + context "and no word delimiters" do + let(:string) { "foobar" } + + include_examples :tokenises_string, to: [["foobar"]] + end + + context "and a trailing path delimiter" do + let(:string) { "foobar/" } + + include_examples :tokenises_string, to: [["foobar"]] + end + + context "and a trailing namespace delimiter" do + let(:string) { "foobar::" } + + include_examples :tokenises_string, to: [["foobar"]] + end + + context "and a leading path delimiter" do + let(:string) { "/foobar" } + + include_examples :tokenises_string, to: [[], ["foobar"]] + end + + context "and a leading namespace delimiter" do + let(:string) { "::foobar" } + + include_examples :tokenises_string, to: [[], ["foobar"]] + end + + context "and leading and trailing path delimiters" do + let(:string) { "/foobar/" } + + include_examples :tokenises_string, to: [[], ["foobar"]] + end + + context "and leading and trailing namespace delimiters" do + let(:string) { "::foobar::" } + + include_examples :tokenises_string, to: [[], ["foobar"]] + end + end + + context "when string has two words" do + context "and a trailing path delimiter" do + let(:string) { "foo/bar/" } + + include_examples :tokenises_string, to: [["foo"], ["bar"]] + end + + context "and a trailing namespace delimiter" do + let(:string) { "foo::bar::" } + + include_examples :tokenises_string, to: [["foo"], ["bar"]] + end + + context "and a leading path delimiter" do + let(:string) { "/foo/bar" } + + include_examples :tokenises_string, to: [[], ["foo"], ["bar"]] + end + + context "and a leading namespace delimiter" do + let(:string) { "::foo::bar" } + + include_examples :tokenises_string, to: [[], ["foo"], ["bar"]] + end + + context "and leading and trailing path delimiters" do + let(:string) { "/foo/bar/" } + + include_examples :tokenises_string, to: [[], ["foo"], ["bar"]] + end + + context "and leading and trailing namespace delimiters" do + let(:string) { "::foo::bar::" } + + include_examples :tokenises_string, to: [[], ["foo"], ["bar"]] + end + end + end + + describe "splitting tokens" do + context "when word contains two tokens" do + context "and tokens delimited with an underscore" do + let(:string) { "foo_bar" } + + include_examples :tokenises_string, to: [%w[foo bar]] + end + + context "and tokens delimited with a hyphen" do + let(:string) { "foo-bar" } + + include_examples :tokenises_string, to: [%w[foo bar]] + end + + context "and tokens delimited using camel case" do + let(:string) { "fooBar" } + + include_examples :tokenises_string, to: [%w[foo Bar]] + end + + context "and tokens delimited using pascal case" do + let(:string) { "FooBar" } + + include_examples :tokenises_string, to: [%w[Foo Bar]] + end + + context "and tokens delimited using screaming snake case" do + let(:string) { "FOO_BAR" } + + include_examples :tokenises_string, to: [%w[FOO BAR]] + end + + context "and token delimiters are repeated" do + let(:string) { "foo__bar" } + + include_examples :tokenises_string, to: [%w[foo bar]] + end + end + end + + describe "splitting groups and tokens" do + [ + { in: "foo/bar_baz.rb", out: [["foo"], %w[bar baz.rb]] }, + { in: "/foo/bar_baz.rb", out: [[], ["foo"], %w[bar baz.rb]] }, + { in: "Foo::BarBaz", out: [["Foo"], %w[Bar Baz]] }, + { in: "::Foo::BarBaz", out: [[], ["Foo"], %w[Bar Baz]] } + ].each do |params| + context "when #{params[:in]}" do + let(:string) { params[:in] } + + include_examples :tokenises_string, to: params[:out] + end + end + end + end +end From 688980bf5ab922a97326f627713be2aead8fc75c Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Fri, 15 Sep 2023 20:41:31 +0100 Subject: [PATCH 02/17] Implement inflector token transformation --- lib/kangaru/inflectors/inflector.rb | 35 ++++++++++++ sig/kangaru/inflectors/inflector.rbs | 19 +++++++ spec/kangaru/inflectors/inflector_spec.rb | 68 +++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 lib/kangaru/inflectors/inflector.rb create mode 100644 sig/kangaru/inflectors/inflector.rbs create mode 100644 spec/kangaru/inflectors/inflector_spec.rb diff --git a/lib/kangaru/inflectors/inflector.rb b/lib/kangaru/inflectors/inflector.rb new file mode 100644 index 0000000..6437e3d --- /dev/null +++ b/lib/kangaru/inflectors/inflector.rb @@ -0,0 +1,35 @@ +module Kangaru + module Inflectors + class Inflector + attr_reader :tokeniser + + def initialize(string) + @tokeniser = Tokeniser.new(string) + end + + def inflect + tokeniser.split.map do |tokens| + tokens.map { |token| transform_token(token) }.join + end.join + end + + private + + def class_attribute(key) + self.class.instance_variable_get(:"@#{key}") + end + + def token_transformer + class_attribute(:token_transformer) + end + + def transform_token(token) + case token_transformer + when Proc then token_transformer.call(token) + when Symbol then token.send(token_transformer) + else token + end + end + end + end +end diff --git a/sig/kangaru/inflectors/inflector.rbs b/sig/kangaru/inflectors/inflector.rbs new file mode 100644 index 0000000..f8442b0 --- /dev/null +++ b/sig/kangaru/inflectors/inflector.rbs @@ -0,0 +1,19 @@ +module Kangaru + module Inflectors + class Inflector + attr_reader tokeniser: Tokeniser + + def initialize: (String) -> void + + def inflect: -> String + + private + + def class_attribute: (Symbol) -> untyped + + def token_transformer: -> untyped + + def transform_token: (String) -> String + end + end +end diff --git a/spec/kangaru/inflectors/inflector_spec.rb b/spec/kangaru/inflectors/inflector_spec.rb new file mode 100644 index 0000000..8e5c333 --- /dev/null +++ b/spec/kangaru/inflectors/inflector_spec.rb @@ -0,0 +1,68 @@ +RSpec.describe Kangaru::Inflectors::Inflector do + subject(:inflector) { described_class.new(string) } + + let(:string) { nil } + + let(:tokeniser) do + instance_double(Kangaru::Inflectors::Tokeniser, split: token_groups) + end + + let(:inflector_params) do + { + "@token_transformer": token_transformer + }.compact + end + + let(:token_transformer) { nil } + + around do |spec| + inflector_params.each do |key, value| + described_class.instance_variable_set(key, value) + end + + spec.run + + inflector_params.each_key do |key| + described_class.remove_instance_variable(key) + end + end + + before do + allow(Kangaru::Inflectors::Tokeniser).to receive(:new).and_return(tokeniser) + end + + describe "#inflect" do + subject(:inflection) { inflector.inflect } + + context "when string consists of one word" do + let(:token_groups) { [tokens] } + + context "and word consists of one token" do + let(:tokens) { [token] } + let(:token) { "foobar" } + + context "and no token transformer is set" do + let(:token_transformer) { nil } + + include_examples :inflects, to: "foobar" + end + + context "and token transformer is a proc" do + let(:token_transformer) { ->(token) { token.reverse } } + + it "returns the expected inflection" do + expect(inflection).to eq(token.reverse) + end + end + + context "and token transformer is a symbol" do + let(:token_transformer) { :upcase } + + it "returns the expected inflection" do + expect(inflection).to eq(token.upcase) + end + end + end + end + end +end From 97295d19d6d7f154776473af120557fa45c4cab5 Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Fri, 15 Sep 2023 20:58:25 +0100 Subject: [PATCH 03/17] Extract inflection examples into shared examples --- spec/kangaru/inflectors/inflector_spec.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/spec/kangaru/inflectors/inflector_spec.rb b/spec/kangaru/inflectors/inflector_spec.rb index 8e5c333..440a69c 100644 --- a/spec/kangaru/inflectors/inflector_spec.rb +++ b/spec/kangaru/inflectors/inflector_spec.rb @@ -34,6 +34,12 @@ describe "#inflect" do subject(:inflection) { inflector.inflect } + shared_examples :inflects do |options| + it "inflects to #{options[:to]}" do + expect(inflection).to eq(options[:to]) + end + end + context "when string consists of one word" do let(:token_groups) { [tokens] } @@ -50,17 +56,13 @@ context "and token transformer is a proc" do let(:token_transformer) { ->(token) { token.reverse } } - it "returns the expected inflection" do - expect(inflection).to eq(token.reverse) - end + include_examples :inflects, to: "raboof" end context "and token transformer is a symbol" do let(:token_transformer) { :upcase } - it "returns the expected inflection" do - expect(inflection).to eq(token.upcase) - end + include_examples :inflects, to: "FOOBAR" end end end From 8b8eddf2832b4a7b3f2c4ddf455857bae76dddeb Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Fri, 15 Sep 2023 21:17:28 +0100 Subject: [PATCH 04/17] Implement inflector token joining --- lib/kangaru/inflectors/inflector.rb | 12 ++++- sig/kangaru/inflectors/inflector.rbs | 4 ++ spec/kangaru/inflectors/inflector_spec.rb | 56 ++++++++++++++++++++++- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/lib/kangaru/inflectors/inflector.rb b/lib/kangaru/inflectors/inflector.rb index 6437e3d..ff5e637 100644 --- a/lib/kangaru/inflectors/inflector.rb +++ b/lib/kangaru/inflectors/inflector.rb @@ -9,7 +9,9 @@ def initialize(string) def inflect tokeniser.split.map do |tokens| - tokens.map { |token| transform_token(token) }.join + tokens.map! { |token| transform_token(token) } + + join_tokens(tokens) end.join end @@ -23,6 +25,10 @@ def token_transformer class_attribute(:token_transformer) end + def token_joiner + class_attribute(:token_joiner) + end + def transform_token(token) case token_transformer when Proc then token_transformer.call(token) @@ -30,6 +36,10 @@ def transform_token(token) else token end end + + def join_tokens(tokens) + tokens.join(token_joiner) + end end end end diff --git a/sig/kangaru/inflectors/inflector.rbs b/sig/kangaru/inflectors/inflector.rbs index f8442b0..155be41 100644 --- a/sig/kangaru/inflectors/inflector.rbs +++ b/sig/kangaru/inflectors/inflector.rbs @@ -13,7 +13,11 @@ module Kangaru def token_transformer: -> untyped + def token_joiner: -> untyped + def transform_token: (String) -> String + + def join_tokens: (Tokeniser::token_group) -> String end end end diff --git a/spec/kangaru/inflectors/inflector_spec.rb b/spec/kangaru/inflectors/inflector_spec.rb index 440a69c..3db8c09 100644 --- a/spec/kangaru/inflectors/inflector_spec.rb +++ b/spec/kangaru/inflectors/inflector_spec.rb @@ -9,11 +9,13 @@ let(:inflector_params) do { - "@token_transformer": token_transformer + "@token_transformer": token_transformer, + "@token_joiner": token_joiner }.compact end let(:token_transformer) { nil } + let(:token_joiner) { nil } around do |spec| inflector_params.each do |key, value| @@ -65,6 +67,58 @@ include_examples :inflects, to: "FOOBAR" end end + + context "and word consists of multiple tokens" do + let(:tokens) { %w[foo bar baz] } + + context "and token transformer is not set" do + let(:token_transformer) { nil } + + context "and token joiner is not set" do + let(:token_joiner) { nil } + + include_examples :inflects, to: "foobarbaz" + end + + context "and token joiner is set" do + let(:token_joiner) { "_" } + + include_examples :inflects, to: "foo_bar_baz" + end + end + + context "and token transformer is a proc" do + let(:token_transformer) { ->(token) { token.capitalize } } + + context "and token joiner is not set" do + let(:token_joiner) { nil } + + include_examples :inflects, to: "FooBarBaz" + end + + context "and token joiner is set" do + let(:token_joiner) { "_" } + + include_examples :inflects, to: "Foo_Bar_Baz" + end + end + + context "and token transformer is a symbol" do + let(:token_transformer) { :upcase } + + context "and token joiner is not set" do + let(:token_joiner) { nil } + + include_examples :inflects, to: "FOOBARBAZ" + end + + context "and token joiner is set" do + let(:token_joiner) { "_" } + + include_examples :inflects, to: "FOO_BAR_BAZ" + end + end + end end end end From b8b8b54a240e4cefdc4ac6ca53e2d51f2c70bccd Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 00:13:47 +0100 Subject: [PATCH 05/17] Implement inflector group joining --- lib/kangaru/inflectors/inflector.rb | 26 +++- sig/kangaru/inflectors/inflector.rbs | 12 +- spec/kangaru/inflectors/inflector_spec.rb | 182 +++++++++++++++++++++- 3 files changed, 213 insertions(+), 7 deletions(-) diff --git a/lib/kangaru/inflectors/inflector.rb b/lib/kangaru/inflectors/inflector.rb index ff5e637..4e907d4 100644 --- a/lib/kangaru/inflectors/inflector.rb +++ b/lib/kangaru/inflectors/inflector.rb @@ -1,6 +1,8 @@ module Kangaru module Inflectors class Inflector + DEFAULT_GROUP_JOINER = "/".freeze + attr_reader :tokeniser def initialize(string) @@ -8,11 +10,9 @@ def initialize(string) end def inflect - tokeniser.split.map do |tokens| - tokens.map! { |token| transform_token(token) } - - join_tokens(tokens) - end.join + join_groups( + transform_and_join_tokens(tokeniser.split) + ) end private @@ -29,6 +29,18 @@ def token_joiner class_attribute(:token_joiner) end + def group_joiner + class_attribute(:group_joiner) || DEFAULT_GROUP_JOINER + end + + def transform_and_join_tokens(token_groups) + token_groups.map do |tokens| + join_tokens( + tokens.map { |token| transform_token(token) } + ) + end + end + def transform_token(token) case token_transformer when Proc then token_transformer.call(token) @@ -40,6 +52,10 @@ def transform_token(token) def join_tokens(tokens) tokens.join(token_joiner) end + + def join_groups(words) + words.join(group_joiner) + end end end end diff --git a/sig/kangaru/inflectors/inflector.rbs b/sig/kangaru/inflectors/inflector.rbs index 155be41..b3a5912 100644 --- a/sig/kangaru/inflectors/inflector.rbs +++ b/sig/kangaru/inflectors/inflector.rbs @@ -1,6 +1,10 @@ module Kangaru module Inflectors class Inflector + type token_groups = Array[Array[String]] + + DEFAULT_GROUP_JOINER: String + attr_reader tokeniser: Tokeniser def initialize: (String) -> void @@ -15,9 +19,15 @@ module Kangaru def token_joiner: -> untyped + def group_joiner: -> untyped + + def transform_and_join_tokens: (Array[Array[String]]) -> Array[String] + def transform_token: (String) -> String - def join_tokens: (Tokeniser::token_group) -> String + def join_tokens: (Array[String]) -> String + + def join_groups: (Array[String]) -> String end end end diff --git a/spec/kangaru/inflectors/inflector_spec.rb b/spec/kangaru/inflectors/inflector_spec.rb index 3db8c09..f6941ab 100644 --- a/spec/kangaru/inflectors/inflector_spec.rb +++ b/spec/kangaru/inflectors/inflector_spec.rb @@ -10,12 +10,14 @@ let(:inflector_params) do { "@token_transformer": token_transformer, - "@token_joiner": token_joiner + "@token_joiner": token_joiner, + "@group_joiner": group_joiner }.compact end let(:token_transformer) { nil } let(:token_joiner) { nil } + let(:group_joiner) { nil } around do |spec| inflector_params.each do |key, value| @@ -120,5 +122,183 @@ end end end + + context "when string consists of multiple words" do + context "and first word is empty" do + let(:token_groups) { [[], %w[foo bar], %w[baz]] } + + context "and token transformer is not set" do + let(:token_transformer) { nil } + + context "and token joiner is not set" do + let(:token_joiner) { nil } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "/foobar/baz" + end + + context "and group joiner is set" do + let(:group_joiner) { "::" } + + include_examples :inflects, to: "::foobar::baz" + end + end + + context "and token joiner is set" do + let(:token_joiner) { "." } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "/foo.bar/baz" + end + + context "and group joiner is set" do + let(:group_joiner) { "::" } + + include_examples :inflects, to: "::foo.bar::baz" + end + end + end + + context "and token transformer is a proc" do + let(:token_transformer) { ->(token) { token.capitalize } } + + context "and token joiner is not set" do + let(:token_joiner) { nil } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "/FooBar/Baz" + end + end + + context "and token joiner is set" do + let(:token_joiner) { "-" } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "/Foo-Bar/Baz" + end + end + end + + context "and token transformer is a symbol" do + let(:token_transformer) { :capitalize } + + context "and token joiner is not set" do + let(:token_joiner) { nil } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "/FooBar/Baz" + end + end + + context "and token joiner is set" do + let(:token_joiner) { "-" } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "/Foo-Bar/Baz" + end + end + end + end + + context "and first word is not empty" do + let(:token_groups) { [%w[foo bar], %w[baz]] } + + context "and token transformer is not set" do + let(:token_transformer) { nil } + + context "and token joiner is not set" do + let(:token_joiner) { nil } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "foobar/baz" + end + + context "and group joiner is set" do + let(:group_joiner) { "::" } + + include_examples :inflects, to: "foobar::baz" + end + end + + context "and token joiner is set" do + let(:token_joiner) { "." } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "foo.bar/baz" + end + + context "and group joiner is set" do + let(:group_joiner) { "::" } + + include_examples :inflects, to: "foo.bar::baz" + end + end + end + + context "and token transformer is a proc" do + let(:token_transformer) { ->(token) { token.capitalize } } + + context "and token joiner is not set" do + let(:token_joiner) { nil } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "FooBar/Baz" + end + end + + context "and token joiner is set" do + let(:token_joiner) { "-" } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "Foo-Bar/Baz" + end + end + end + + context "and token transformer is a symbol" do + let(:token_transformer) { :capitalize } + + context "and token joiner is not set" do + let(:token_joiner) { nil } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "FooBar/Baz" + end + end + + context "and token joiner is set" do + let(:token_joiner) { "-" } + + context "and group joiner is not set" do + let(:group_joiner) { nil } + + include_examples :inflects, to: "Foo-Bar/Baz" + end + end + end + end + end end end From 17c2d9ea160b781539e6952f3817a7aaee4e59a0 Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 00:43:23 +0100 Subject: [PATCH 06/17] Implement inflector macro interface --- lib/kangaru/inflectors/inflector.rb | 2 + lib/kangaru/inflectors/inflector_macros.rb | 17 +++++ sig/kangaru/inflectors/inflector.rbs | 2 + sig/kangaru/inflectors/inflector_macros.rbs | 15 +++++ .../inflectors/inflector_macros_spec.rb | 67 +++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 lib/kangaru/inflectors/inflector_macros.rb create mode 100644 sig/kangaru/inflectors/inflector_macros.rbs create mode 100644 spec/kangaru/inflectors/inflector_macros_spec.rb diff --git a/lib/kangaru/inflectors/inflector.rb b/lib/kangaru/inflectors/inflector.rb index 4e907d4..437d965 100644 --- a/lib/kangaru/inflectors/inflector.rb +++ b/lib/kangaru/inflectors/inflector.rb @@ -1,6 +1,8 @@ module Kangaru module Inflectors class Inflector + extend InflectorMacros + DEFAULT_GROUP_JOINER = "/".freeze attr_reader :tokeniser diff --git a/lib/kangaru/inflectors/inflector_macros.rb b/lib/kangaru/inflectors/inflector_macros.rb new file mode 100644 index 0000000..b07e75a --- /dev/null +++ b/lib/kangaru/inflectors/inflector_macros.rb @@ -0,0 +1,17 @@ +module Kangaru + module Inflectors + module InflectorMacros + def transform_tokens_with(symbol = nil, &block) + @token_transformer = symbol || block + end + + def join_tokens_with(joiner) + @token_joiner = joiner + end + + def join_groups_with(joiner) + @group_joiner = joiner + end + end + end +end diff --git a/sig/kangaru/inflectors/inflector.rbs b/sig/kangaru/inflectors/inflector.rbs index b3a5912..d52e155 100644 --- a/sig/kangaru/inflectors/inflector.rbs +++ b/sig/kangaru/inflectors/inflector.rbs @@ -3,6 +3,8 @@ module Kangaru class Inflector type token_groups = Array[Array[String]] + extend InflectorMacros + DEFAULT_GROUP_JOINER: String attr_reader tokeniser: Tokeniser diff --git a/sig/kangaru/inflectors/inflector_macros.rbs b/sig/kangaru/inflectors/inflector_macros.rbs new file mode 100644 index 0000000..94af986 --- /dev/null +++ b/sig/kangaru/inflectors/inflector_macros.rbs @@ -0,0 +1,15 @@ +module Kangaru + module Inflectors + module InflectorMacros + @token_transformer: untyped + @token_joiner: untyped + @group_joiner: untyped + + def transform_tokens_with: (?Symbol?) ?{ (String) -> String } -> void + + def join_tokens_with: (String) -> void + + def join_groups_with: (String) -> void + end + end +end diff --git a/spec/kangaru/inflectors/inflector_macros_spec.rb b/spec/kangaru/inflectors/inflector_macros_spec.rb new file mode 100644 index 0000000..1b6f363 --- /dev/null +++ b/spec/kangaru/inflectors/inflector_macros_spec.rb @@ -0,0 +1,67 @@ +RSpec.describe Kangaru::Inflectors::InflectorMacros do + subject(:target_class) do + Class.new { extend Kangaru::Inflectors::InflectorMacros } + end + + describe "#transform_tokens_with" do + context "when called with a proc" do + subject(:transform_tokens_with) do + target_class.transform_tokens_with(&block) + end + + let(:block) { ->(token) { token.upcase } } + + it "sets the instance variable" do + expect { transform_tokens_with } + .to change { target_class.instance_variable_get(:@token_transformer) } + .from(nil) + .to(block) + end + end + + context "when calledd iwth a symbol" do + subject(:transform_tokens_with) do + target_class.transform_tokens_with(symbol) + end + + let(:symbol) { :method } + + it "sets the instance variable" do + expect { transform_tokens_with } + .to change { target_class.instance_variable_get(:@token_transformer) } + .from(nil) + .to(symbol) + end + end + end + + describe "#join_tokens_with" do + subject(:join_tokens_with) do + target_class.join_tokens_with(joiner) + end + + let(:joiner) { "." } + + it "sets the instance variable" do + expect { join_tokens_with } + .to change { target_class.instance_variable_get(:@token_joiner) } + .from(nil) + .to(joiner) + end + end + + describe "#join_groups_with" do + subject(:join_groups_with) do + target_class.join_groups_with(joiner) + end + + let(:joiner) { "." } + + it "sets the instance variable" do + expect { join_groups_with } + .to change { target_class.instance_variable_get(:@group_joiner) } + .from(nil) + .to(joiner) + end + end +end From 245814ff8b70a65c4a8ba31993c6cda353fdceda Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 01:43:26 +0100 Subject: [PATCH 07/17] Restructure inflector spec --- spec/kangaru/inflectors/inflector_spec.rb | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/spec/kangaru/inflectors/inflector_spec.rb b/spec/kangaru/inflectors/inflector_spec.rb index f6941ab..e425ff3 100644 --- a/spec/kangaru/inflectors/inflector_spec.rb +++ b/spec/kangaru/inflectors/inflector_spec.rb @@ -1,11 +1,7 @@ RSpec.describe Kangaru::Inflectors::Inflector do subject(:inflector) { described_class.new(string) } - let(:string) { nil } - - let(:tokeniser) do - instance_double(Kangaru::Inflectors::Tokeniser, split: token_groups) - end + let(:string) { "foobarbaz" } let(:inflector_params) do { @@ -31,13 +27,19 @@ end end - before do - allow(Kangaru::Inflectors::Tokeniser).to receive(:new).and_return(tokeniser) - end - describe "#inflect" do subject(:inflection) { inflector.inflect } + let(:tokeniser) do + instance_double(Kangaru::Inflectors::Tokeniser, split: token_groups) + end + + before do + allow(Kangaru::Inflectors::Tokeniser) + .to receive(:new) + .and_return(tokeniser) + end + shared_examples :inflects do |options| it "inflects to #{options[:to]}" do expect(inflection).to eq(options[:to]) From 164b40b19efa113cb8910b1c770db3d511789504 Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 01:45:06 +0100 Subject: [PATCH 08/17] Move tokeniser creation out of inflector initializer --- lib/kangaru/inflectors/inflector.rb | 8 ++++++-- sig/kangaru/inflectors/inflector.rbs | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/kangaru/inflectors/inflector.rb b/lib/kangaru/inflectors/inflector.rb index 437d965..e910993 100644 --- a/lib/kangaru/inflectors/inflector.rb +++ b/lib/kangaru/inflectors/inflector.rb @@ -5,10 +5,10 @@ class Inflector DEFAULT_GROUP_JOINER = "/".freeze - attr_reader :tokeniser + attr_reader :string def initialize(string) - @tokeniser = Tokeniser.new(string) + @string = string end def inflect @@ -19,6 +19,10 @@ def inflect private + def tokeniser + @tokeniser ||= Tokeniser.new(string) + end + def class_attribute(key) self.class.instance_variable_get(:"@#{key}") end diff --git a/sig/kangaru/inflectors/inflector.rbs b/sig/kangaru/inflectors/inflector.rbs index d52e155..798e50f 100644 --- a/sig/kangaru/inflectors/inflector.rbs +++ b/sig/kangaru/inflectors/inflector.rbs @@ -7,7 +7,7 @@ module Kangaru DEFAULT_GROUP_JOINER: String - attr_reader tokeniser: Tokeniser + attr_reader string: String def initialize: (String) -> void @@ -15,6 +15,8 @@ module Kangaru private + attr_reader tokeniser: Tokeniser + def class_attribute: (Symbol) -> untyped def token_transformer: -> untyped From f2ef71dd16583fec4b8919e40bd672eaf284f1f5 Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 02:11:02 +0100 Subject: [PATCH 09/17] Implement inflector input filtering --- lib/kangaru/inflectors/inflector.rb | 13 +++++++++++- lib/kangaru/inflectors/inflector_macros.rb | 4 ++++ sig/kangaru/inflectors/inflector.rbs | 4 ++++ sig/kangaru/inflectors/inflector_macros.rbs | 3 +++ .../inflectors/inflector_macros_spec.rb | 15 ++++++++++++++ spec/kangaru/inflectors/inflector_spec.rb | 20 +++++++++++++++++++ 6 files changed, 58 insertions(+), 1 deletion(-) diff --git a/lib/kangaru/inflectors/inflector.rb b/lib/kangaru/inflectors/inflector.rb index e910993..50e057e 100644 --- a/lib/kangaru/inflectors/inflector.rb +++ b/lib/kangaru/inflectors/inflector.rb @@ -8,7 +8,7 @@ class Inflector attr_reader :string def initialize(string) - @string = string + @string = filter_input(string) end def inflect @@ -27,6 +27,10 @@ def class_attribute(key) self.class.instance_variable_get(:"@#{key}") end + def input_filter + class_attribute(:input_filter) + end + def token_transformer class_attribute(:token_transformer) end @@ -39,6 +43,13 @@ def group_joiner class_attribute(:group_joiner) || DEFAULT_GROUP_JOINER end + def filter_input(input) + case input_filter + when Regexp then input.gsub(input_filter, "") + else input + end + end + def transform_and_join_tokens(token_groups) token_groups.map do |tokens| join_tokens( diff --git a/lib/kangaru/inflectors/inflector_macros.rb b/lib/kangaru/inflectors/inflector_macros.rb index b07e75a..940455c 100644 --- a/lib/kangaru/inflectors/inflector_macros.rb +++ b/lib/kangaru/inflectors/inflector_macros.rb @@ -1,6 +1,10 @@ module Kangaru module Inflectors module InflectorMacros + def filter_input_with(pattern) + @input_filter = pattern + end + def transform_tokens_with(symbol = nil, &block) @token_transformer = symbol || block end diff --git a/sig/kangaru/inflectors/inflector.rbs b/sig/kangaru/inflectors/inflector.rbs index 798e50f..e85c72a 100644 --- a/sig/kangaru/inflectors/inflector.rbs +++ b/sig/kangaru/inflectors/inflector.rbs @@ -19,12 +19,16 @@ module Kangaru def class_attribute: (Symbol) -> untyped + def input_filter: -> untyped + def token_transformer: -> untyped def token_joiner: -> untyped def group_joiner: -> untyped + def filter_input: (String) -> String + def transform_and_join_tokens: (Array[Array[String]]) -> Array[String] def transform_token: (String) -> String diff --git a/sig/kangaru/inflectors/inflector_macros.rbs b/sig/kangaru/inflectors/inflector_macros.rbs index 94af986..5b20dc8 100644 --- a/sig/kangaru/inflectors/inflector_macros.rbs +++ b/sig/kangaru/inflectors/inflector_macros.rbs @@ -1,10 +1,13 @@ module Kangaru module Inflectors module InflectorMacros + @input_filter: untyped @token_transformer: untyped @token_joiner: untyped @group_joiner: untyped + def filter_input_with: (Regexp) -> void + def transform_tokens_with: (?Symbol?) ?{ (String) -> String } -> void def join_tokens_with: (String) -> void diff --git a/spec/kangaru/inflectors/inflector_macros_spec.rb b/spec/kangaru/inflectors/inflector_macros_spec.rb index 1b6f363..672c5c9 100644 --- a/spec/kangaru/inflectors/inflector_macros_spec.rb +++ b/spec/kangaru/inflectors/inflector_macros_spec.rb @@ -3,6 +3,21 @@ Class.new { extend Kangaru::Inflectors::InflectorMacros } end + describe "#filter_input_with" do + subject(:filter_input_with) do + target_class.filter_input_with(pattern) + end + + let(:pattern) { /./ } + + it "sets the instance variable" do + expect { filter_input_with } + .to change { target_class.instance_variable_get(:@input_filter) } + .from(nil) + .to(pattern) + end + end + describe "#transform_tokens_with" do context "when called with a proc" do subject(:transform_tokens_with) do diff --git a/spec/kangaru/inflectors/inflector_spec.rb b/spec/kangaru/inflectors/inflector_spec.rb index e425ff3..04ae59f 100644 --- a/spec/kangaru/inflectors/inflector_spec.rb +++ b/spec/kangaru/inflectors/inflector_spec.rb @@ -5,12 +5,14 @@ let(:inflector_params) do { + "@input_filter": input_filter, "@token_transformer": token_transformer, "@token_joiner": token_joiner, "@group_joiner": group_joiner }.compact end + let(:input_filter) { nil } let(:token_transformer) { nil } let(:token_joiner) { nil } let(:group_joiner) { nil } @@ -27,6 +29,24 @@ end end + describe "#initialize" do + context "when input filter is not set" do + let(:input_filter) { nil } + + it "sets the string to the given value" do + expect(inflector.string).to eq(string) + end + end + + context "when input filter is set" do + let(:input_filter) { /[ba]/ } + + it "filters the input" do + expect(inflector.string).to eq("foorz") + end + end + end + describe "#inflect" do subject(:inflection) { inflector.inflect } From 2bedaba2f1729d8c4e868e8567c0b0f4afee0b0c Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 11:13:48 +0100 Subject: [PATCH 10/17] Ensure inflector rules can be inherited --- lib/kangaru/inflectors/inflector_macros.rb | 8 ++++ sig/kangaru/inflectors/inflector_macros.rbs | 2 + .../inflectors/inflector_macros_spec.rb | 45 +++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/lib/kangaru/inflectors/inflector_macros.rb b/lib/kangaru/inflectors/inflector_macros.rb index 940455c..a051235 100644 --- a/lib/kangaru/inflectors/inflector_macros.rb +++ b/lib/kangaru/inflectors/inflector_macros.rb @@ -1,6 +1,14 @@ module Kangaru module Inflectors module InflectorMacros + def inherited(child_class) + instance_variables.each do |rule| + value = instance_variable_get(rule) + + child_class.instance_variable_set(rule, value) + end + end + def filter_input_with(pattern) @input_filter = pattern end diff --git a/sig/kangaru/inflectors/inflector_macros.rbs b/sig/kangaru/inflectors/inflector_macros.rbs index 5b20dc8..b5d95cf 100644 --- a/sig/kangaru/inflectors/inflector_macros.rbs +++ b/sig/kangaru/inflectors/inflector_macros.rbs @@ -6,6 +6,8 @@ module Kangaru @token_joiner: untyped @group_joiner: untyped + def inherited: (Class) -> void + def filter_input_with: (Regexp) -> void def transform_tokens_with: (?Symbol?) ?{ (String) -> String } -> void diff --git a/spec/kangaru/inflectors/inflector_macros_spec.rb b/spec/kangaru/inflectors/inflector_macros_spec.rb index 672c5c9..5e6d103 100644 --- a/spec/kangaru/inflectors/inflector_macros_spec.rb +++ b/spec/kangaru/inflectors/inflector_macros_spec.rb @@ -3,6 +3,51 @@ Class.new { extend Kangaru::Inflectors::InflectorMacros } end + describe "inheriting target classes" do + subject(:inherited_class) { Class.new(target_class) } + + let(:inflection_params) do + { input_filter:, token_transformer:, token_joiner:, group_joiner: } + end + + let(:input_filter) { :foo } + let(:token_transformer) { :bar } + let(:token_joiner) { :baz } + let(:group_joiner) { :far } + + around do |spec| + inflection_params.each do |attribute, value| + target_class.instance_variable_set(:"@#{attribute}", value) + end + + spec.run + + inflection_params.each_key do |attribute| + target_class.remove_instance_variable(:"@#{attribute}") + end + end + + def class_attribute(name) + inherited_class.instance_variable_get(:"@#{name}") + end + + it "forwards the input_filter to the child class" do + expect(class_attribute(:input_filter)).to eq(input_filter) + end + + it "forwards the token_transformer to the child class" do + expect(class_attribute(:token_transformer)).to eq(token_transformer) + end + + it "forwards the token_joiner to the child class" do + expect(class_attribute(:token_joiner)).to eq(token_joiner) + end + + it "forwards the group_joiner to the child class" do + expect(class_attribute(:group_joiner)).to eq(group_joiner) + end + end + describe "#filter_input_with" do subject(:filter_input_with) do target_class.filter_input_with(pattern) From 108ea2db039eac6a0fae9c5d2763389405dfc20d Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 11:44:55 +0100 Subject: [PATCH 11/17] Implement Inflector.inflect static method --- lib/kangaru/inflectors/inflector.rb | 4 ++++ sig/kangaru/inflectors/inflector.rbs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/lib/kangaru/inflectors/inflector.rb b/lib/kangaru/inflectors/inflector.rb index 50e057e..7583268 100644 --- a/lib/kangaru/inflectors/inflector.rb +++ b/lib/kangaru/inflectors/inflector.rb @@ -17,6 +17,10 @@ def inflect ) end + def self.inflect(string) + new(string).inflect + end + private def tokeniser diff --git a/sig/kangaru/inflectors/inflector.rbs b/sig/kangaru/inflectors/inflector.rbs index e85c72a..46bc5e2 100644 --- a/sig/kangaru/inflectors/inflector.rbs +++ b/sig/kangaru/inflectors/inflector.rbs @@ -13,6 +13,8 @@ module Kangaru def inflect: -> String + def self.inflect: (String) -> String + private attr_reader tokeniser: Tokeniser From 3b3f98f3a76b6e9de698ae8451254cb63659eb93 Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 02:13:40 +0100 Subject: [PATCH 12/17] Add inflection shared examples --- spec/support/examples/runs_inflections.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 spec/support/examples/runs_inflections.rb diff --git a/spec/support/examples/runs_inflections.rb b/spec/support/examples/runs_inflections.rb new file mode 100644 index 0000000..208f5b7 --- /dev/null +++ b/spec/support/examples/runs_inflections.rb @@ -0,0 +1,15 @@ +RSpec.shared_examples :runs_inflections do |inflections| + shared_examples :inflects do |from:, to:| + context "when #{from}" do + let(:string) { from } + + it "inflects #{from} to #{to}" do + expect(subject).to eq(to) + end + end + end + + inflections.each do |inflection| + include_examples :inflects, **inflection + end +end From 43342f7a248291d0dab25bf8b4d980e1a5ea109a Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 11:25:15 +0100 Subject: [PATCH 13/17] Implement snakecase inflector --- lib/kangaru/inflectors/snakecase_inflector.rb | 11 +++++ .../inflectors/snakecase_inflector.rbs | 6 +++ .../inflectors/snakecase_inflector_spec.rb | 42 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 lib/kangaru/inflectors/snakecase_inflector.rb create mode 100644 sig/kangaru/inflectors/snakecase_inflector.rbs create mode 100644 spec/kangaru/inflectors/snakecase_inflector_spec.rb diff --git a/lib/kangaru/inflectors/snakecase_inflector.rb b/lib/kangaru/inflectors/snakecase_inflector.rb new file mode 100644 index 0000000..6415710 --- /dev/null +++ b/lib/kangaru/inflectors/snakecase_inflector.rb @@ -0,0 +1,11 @@ +module Kangaru + module Inflectors + class SnakecaseInflector < Inflector + transform_tokens_with :downcase + + join_tokens_with "_" + + join_groups_with "/" + end + end +end diff --git a/sig/kangaru/inflectors/snakecase_inflector.rbs b/sig/kangaru/inflectors/snakecase_inflector.rbs new file mode 100644 index 0000000..aa25967 --- /dev/null +++ b/sig/kangaru/inflectors/snakecase_inflector.rbs @@ -0,0 +1,6 @@ +module Kangaru + module Inflectors + class SnakecaseInflector < Inflector + end + end +end diff --git a/spec/kangaru/inflectors/snakecase_inflector_spec.rb b/spec/kangaru/inflectors/snakecase_inflector_spec.rb new file mode 100644 index 0000000..369838e --- /dev/null +++ b/spec/kangaru/inflectors/snakecase_inflector_spec.rb @@ -0,0 +1,42 @@ +RSpec.describe Kangaru::Inflectors::SnakecaseInflector do + subject(:inflector) { described_class.new(string) } + + describe "#inflect" do + subject(:inflection) { inflector.inflect } + + include_examples :runs_inflections, [ + { from: "foo_bar_baz", to: "foo_bar_baz" }, + { from: "foo_bar__baz", to: "foo_bar_baz" }, + { from: "foo__bar_baz", to: "foo_bar_baz" }, + { from: "foo__bar__baz", to: "foo_bar_baz" }, + + { from: "foo-bar-baz", to: "foo_bar_baz" }, + { from: "foo--bar--baz", to: "foo_bar_baz" }, + { from: "foo-bar--baz", to: "foo_bar_baz" }, + { from: "foo--bar-baz", to: "foo_bar_baz" }, + + { from: "fooBarBaz", to: "foo_bar_baz" }, + { from: "FooBarBaz", to: "foo_bar_baz" }, + { from: "FOOBARBAZ", to: "foobarbaz" }, + { from: "FOO_BAR_BAZ", to: "foo_bar_baz" }, + + { from: "Foo::Bar::Baz", to: "foo/bar/baz" }, + { from: "Foo::BarBaz", to: "foo/bar_baz" }, + { from: "FooBar::Baz", to: "foo_bar/baz" }, + + { from: "::FOOBARBAZ", to: "/foobarbaz" }, + { from: "::Foo::Bar::Baz", to: "/foo/bar/baz" }, + { from: "::Foo::BarBaz", to: "/foo/bar_baz" }, + { from: "::FooBar::Baz", to: "/foo_bar/baz" }, + { from: "::FooBarBaz", to: "/foo_bar_baz" }, + { from: "::FOO_BAR_BAZ", to: "/foo_bar_baz" }, + + { from: "foo_bar_baz.rb", to: "foo_bar_baz.rb" }, + { from: "foo/bar/baz.rb", to: "foo/bar/baz.rb" }, + { from: "foo/barBaz.rb", to: "foo/bar_baz.rb" }, + { from: "/foo_bar_baz.rb", to: "/foo_bar_baz.rb" }, + { from: "/foo/bar/baz.rb", to: "/foo/bar/baz.rb" }, + { from: "/foo/barBaz.rb", to: "/foo/bar_baz.rb" } + ] + end +end From dfe1060e1a81ada6a8a3532973ad13bb06454ced Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 11:32:06 +0100 Subject: [PATCH 14/17] Implement screaming snakecase inflector --- .../screaming_snakecase_inflector.rb | 11 +++++ .../screaming_snakecase_inflector.rbs | 6 +++ .../screaming_snakecase_inflector_spec.rb | 42 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 lib/kangaru/inflectors/screaming_snakecase_inflector.rb create mode 100644 sig/kangaru/inflectors/screaming_snakecase_inflector.rbs create mode 100644 spec/kangaru/inflectors/screaming_snakecase_inflector_spec.rb diff --git a/lib/kangaru/inflectors/screaming_snakecase_inflector.rb b/lib/kangaru/inflectors/screaming_snakecase_inflector.rb new file mode 100644 index 0000000..3e486b1 --- /dev/null +++ b/lib/kangaru/inflectors/screaming_snakecase_inflector.rb @@ -0,0 +1,11 @@ +module Kangaru + module Inflectors + class ScreamingSnakecaseInflector < SnakecaseInflector + join_groups_with "::" + + def inflect + super.upcase + end + end + end +end diff --git a/sig/kangaru/inflectors/screaming_snakecase_inflector.rbs b/sig/kangaru/inflectors/screaming_snakecase_inflector.rbs new file mode 100644 index 0000000..725022b --- /dev/null +++ b/sig/kangaru/inflectors/screaming_snakecase_inflector.rbs @@ -0,0 +1,6 @@ +module Kangaru + module Inflectors + class ScreamingSnakecaseInflector < SnakecaseInflector + end + end +end diff --git a/spec/kangaru/inflectors/screaming_snakecase_inflector_spec.rb b/spec/kangaru/inflectors/screaming_snakecase_inflector_spec.rb new file mode 100644 index 0000000..0069dfb --- /dev/null +++ b/spec/kangaru/inflectors/screaming_snakecase_inflector_spec.rb @@ -0,0 +1,42 @@ +RSpec.describe Kangaru::Inflectors::ScreamingSnakecaseInflector do + subject(:inflector) { described_class.new(string) } + + describe "#inflect" do + subject(:inflection) { inflector.inflect } + + include_examples :runs_inflections, [ + { from: "foo_bar_baz", to: "FOO_BAR_BAZ" }, + { from: "foo_bar__baz", to: "FOO_BAR_BAZ" }, + { from: "foo__bar_baz", to: "FOO_BAR_BAZ" }, + { from: "foo__bar__baz", to: "FOO_BAR_BAZ" }, + + { from: "foo-bar-baz", to: "FOO_BAR_BAZ" }, + { from: "foo--bar--baz", to: "FOO_BAR_BAZ" }, + { from: "foo-bar--baz", to: "FOO_BAR_BAZ" }, + { from: "foo--bar-baz", to: "FOO_BAR_BAZ" }, + + { from: "fooBarBaz", to: "FOO_BAR_BAZ" }, + { from: "FooBarBaz", to: "FOO_BAR_BAZ" }, + { from: "FOOBARBAZ", to: "FOOBARBAZ" }, + { from: "FOO_BAR_BAZ", to: "FOO_BAR_BAZ" }, + + { from: "Foo::Bar::Baz", to: "FOO::BAR::BAZ" }, + { from: "Foo::BarBaz", to: "FOO::BAR_BAZ" }, + { from: "FooBar::Baz", to: "FOO_BAR::BAZ" }, + + { from: "::FOOBARBAZ", to: "::FOOBARBAZ" }, + { from: "::Foo::Bar::Baz", to: "::FOO::BAR::BAZ" }, + { from: "::Foo::BarBaz", to: "::FOO::BAR_BAZ" }, + { from: "::FooBar::Baz", to: "::FOO_BAR::BAZ" }, + { from: "::FooBarBaz", to: "::FOO_BAR_BAZ" }, + { from: "::FOO_BAR_BAZ", to: "::FOO_BAR_BAZ" }, + + { from: "foo_bar_baz.rb", to: "FOO_BAR_BAZ.RB" }, + { from: "foo/bar/baz.rb", to: "FOO::BAR::BAZ.RB" }, + { from: "foo/barBaz.rb", to: "FOO::BAR_BAZ.RB" }, + { from: "/foo_bar_baz.rb", to: "::FOO_BAR_BAZ.RB" }, + { from: "/foo/bar/baz.rb", to: "::FOO::BAR::BAZ.RB" }, + { from: "/foo/barBaz.rb", to: "::FOO::BAR_BAZ.RB" } + ] + end +end From 0ad9db2632a065d6b455f04f4469c8f0fd5a25d9 Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 02:13:52 +0100 Subject: [PATCH 15/17] Implement class inflector --- lib/kangaru/inflectors/class_inflector.rb | 13 ++++++ sig/kangaru/inflectors/class_inflector.rbs | 6 +++ .../inflectors/class_inflector_spec.rb | 42 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 lib/kangaru/inflectors/class_inflector.rb create mode 100644 sig/kangaru/inflectors/class_inflector.rbs create mode 100644 spec/kangaru/inflectors/class_inflector_spec.rb diff --git a/lib/kangaru/inflectors/class_inflector.rb b/lib/kangaru/inflectors/class_inflector.rb new file mode 100644 index 0000000..64eb26b --- /dev/null +++ b/lib/kangaru/inflectors/class_inflector.rb @@ -0,0 +1,13 @@ +module Kangaru + module Inflectors + class ClassInflector < Inflector + filter_input_with(/\.[a-z]+$/) + + transform_tokens_with :capitalize + + join_tokens_with "" + + join_groups_with "::" + end + end +end diff --git a/sig/kangaru/inflectors/class_inflector.rbs b/sig/kangaru/inflectors/class_inflector.rbs new file mode 100644 index 0000000..3cf4d22 --- /dev/null +++ b/sig/kangaru/inflectors/class_inflector.rbs @@ -0,0 +1,6 @@ +module Kangaru + module Inflectors + class ClassInflector < Inflector + end + end +end diff --git a/spec/kangaru/inflectors/class_inflector_spec.rb b/spec/kangaru/inflectors/class_inflector_spec.rb new file mode 100644 index 0000000..d1a8b28 --- /dev/null +++ b/spec/kangaru/inflectors/class_inflector_spec.rb @@ -0,0 +1,42 @@ +RSpec.describe Kangaru::Inflectors::ClassInflector do + subject(:inflector) { described_class.new(string) } + + describe "#inflect" do + subject(:inflection) { inflector.inflect } + + include_examples :runs_inflections, [ + { from: "foo_bar_baz", to: "FooBarBaz" }, + { from: "foo_bar__baz", to: "FooBarBaz" }, + { from: "foo__bar_baz", to: "FooBarBaz" }, + { from: "foo__bar__baz", to: "FooBarBaz" }, + + { from: "foo-bar-baz", to: "FooBarBaz" }, + { from: "foo--bar--baz", to: "FooBarBaz" }, + { from: "foo-bar--baz", to: "FooBarBaz" }, + { from: "foo--bar-baz", to: "FooBarBaz" }, + + { from: "fooBarBaz", to: "FooBarBaz" }, + { from: "FooBarBaz", to: "FooBarBaz" }, + { from: "FOOBARBAZ", to: "Foobarbaz" }, + { from: "FOO_BAR_BAZ", to: "FooBarBaz" }, + + { from: "Foo::Bar::Baz", to: "Foo::Bar::Baz" }, + { from: "Foo::BarBaz", to: "Foo::BarBaz" }, + { from: "FooBar::Baz", to: "FooBar::Baz" }, + + { from: "::FOOBARBAZ", to: "::Foobarbaz" }, + { from: "::Foo::Bar::Baz", to: "::Foo::Bar::Baz" }, + { from: "::Foo::BarBaz", to: "::Foo::BarBaz" }, + { from: "::FooBar::Baz", to: "::FooBar::Baz" }, + { from: "::FooBarBaz", to: "::FooBarBaz" }, + { from: "::FOO_BAR_BAZ", to: "::FooBarBaz" }, + + { from: "foo_bar_baz.rb", to: "FooBarBaz" }, + { from: "foo/bar/baz.rb", to: "Foo::Bar::Baz" }, + { from: "foo/barBaz.rb", to: "Foo::BarBaz" }, + { from: "/foo_bar_baz.rb", to: "::FooBarBaz" }, + { from: "/foo/bar/baz.rb", to: "::Foo::Bar::Baz" }, + { from: "/foo/barBaz.rb", to: "::Foo::BarBaz" } + ] + end +end From ede719df31eaba0f58cb99a8ff0fb70ab99d0047 Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 11:07:00 +0100 Subject: [PATCH 16/17] Implement path inflector --- lib/kangaru/inflectors/path_inflector.rb | 21 +++++ sig/kangaru/inflectors/path_inflector.rbs | 7 ++ .../kangaru/inflectors/path_inflector_spec.rb | 87 +++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 lib/kangaru/inflectors/path_inflector.rb create mode 100644 sig/kangaru/inflectors/path_inflector.rbs create mode 100644 spec/kangaru/inflectors/path_inflector_spec.rb diff --git a/lib/kangaru/inflectors/path_inflector.rb b/lib/kangaru/inflectors/path_inflector.rb new file mode 100644 index 0000000..327ad39 --- /dev/null +++ b/lib/kangaru/inflectors/path_inflector.rb @@ -0,0 +1,21 @@ +module Kangaru + module Inflectors + class PathInflector < Inflector + filter_input_with(/\.[a-z]+$/) + + transform_tokens_with :downcase + + join_tokens_with "_" + + join_groups_with "/" + + def inflect(with_ext: nil) + inflection = super() + + return inflection unless with_ext + + "#{inflection}.#{with_ext}" + end + end + end +end diff --git a/sig/kangaru/inflectors/path_inflector.rbs b/sig/kangaru/inflectors/path_inflector.rbs new file mode 100644 index 0000000..312311d --- /dev/null +++ b/sig/kangaru/inflectors/path_inflector.rbs @@ -0,0 +1,7 @@ +module Kangaru + module Inflectors + class PathInflector < Inflector + def inflect: (?with_ext: String?) -> String + end + end +end diff --git a/spec/kangaru/inflectors/path_inflector_spec.rb b/spec/kangaru/inflectors/path_inflector_spec.rb new file mode 100644 index 0000000..5d0d094 --- /dev/null +++ b/spec/kangaru/inflectors/path_inflector_spec.rb @@ -0,0 +1,87 @@ +RSpec.describe Kangaru::Inflectors::PathInflector do + subject(:inflector) { described_class.new(string) } + + describe "#inflect" do + subject(:inflection) { inflector.inflect(**options) } + + let(:options) { { with_ext: }.compact } + + context "when extension is not specified" do + let(:with_ext) { nil } + + include_examples :runs_inflections, [ + { from: "foo_bar_baz", to: "foo_bar_baz" }, + { from: "foo_bar__baz", to: "foo_bar_baz" }, + { from: "foo__bar_baz", to: "foo_bar_baz" }, + { from: "foo__bar__baz", to: "foo_bar_baz" }, + + { from: "foo-bar-baz", to: "foo_bar_baz" }, + { from: "foo--bar--baz", to: "foo_bar_baz" }, + { from: "foo-bar--baz", to: "foo_bar_baz" }, + { from: "foo--bar-baz", to: "foo_bar_baz" }, + + { from: "fooBarBaz", to: "foo_bar_baz" }, + { from: "FooBarBaz", to: "foo_bar_baz" }, + { from: "FOOBARBAZ", to: "foobarbaz" }, + { from: "FOO_BAR_BAZ", to: "foo_bar_baz" }, + + { from: "Foo::Bar::Baz", to: "foo/bar/baz" }, + { from: "Foo::BarBaz", to: "foo/bar_baz" }, + { from: "FooBar::Baz", to: "foo_bar/baz" }, + + { from: "::FOOBARBAZ", to: "/foobarbaz" }, + { from: "::Foo::Bar::Baz", to: "/foo/bar/baz" }, + { from: "::Foo::BarBaz", to: "/foo/bar_baz" }, + { from: "::FooBar::Baz", to: "/foo_bar/baz" }, + { from: "::FooBarBaz", to: "/foo_bar_baz" }, + { from: "::FOO_BAR_BAZ", to: "/foo_bar_baz" }, + + { from: "foo_bar_baz.rb", to: "foo_bar_baz" }, + { from: "foo/bar/baz.rb", to: "foo/bar/baz" }, + { from: "foo/barBaz.rb", to: "foo/bar_baz" }, + { from: "/foo_bar_baz.rb", to: "/foo_bar_baz" }, + { from: "/foo/bar/baz.rb", to: "/foo/bar/baz" }, + { from: "/foo/barBaz.rb", to: "/foo/bar_baz" } + ] + end + + context "when extension is specified" do + let(:with_ext) { "rb" } + + include_examples :runs_inflections, [ + { from: "foo_bar_baz", to: "foo_bar_baz.rb" }, + { from: "foo_bar__baz", to: "foo_bar_baz.rb" }, + { from: "foo__bar_baz", to: "foo_bar_baz.rb" }, + { from: "foo__bar__baz", to: "foo_bar_baz.rb" }, + + { from: "foo-bar-baz", to: "foo_bar_baz.rb" }, + { from: "foo--bar--baz", to: "foo_bar_baz.rb" }, + { from: "foo-bar--baz", to: "foo_bar_baz.rb" }, + { from: "foo--bar-baz", to: "foo_bar_baz.rb" }, + + { from: "fooBarBaz", to: "foo_bar_baz.rb" }, + { from: "FooBarBaz", to: "foo_bar_baz.rb" }, + { from: "FOOBARBAZ", to: "foobarbaz.rb" }, + { from: "FOO_BAR_BAZ", to: "foo_bar_baz.rb" }, + + { from: "Foo::Bar::Baz", to: "foo/bar/baz.rb" }, + { from: "Foo::BarBaz", to: "foo/bar_baz.rb" }, + { from: "FooBar::Baz", to: "foo_bar/baz.rb" }, + + { from: "::FOOBARBAZ", to: "/foobarbaz.rb" }, + { from: "::Foo::Bar::Baz", to: "/foo/bar/baz.rb" }, + { from: "::Foo::BarBaz", to: "/foo/bar_baz.rb" }, + { from: "::FooBar::Baz", to: "/foo_bar/baz.rb" }, + { from: "::FooBarBaz", to: "/foo_bar_baz.rb" }, + { from: "::FOO_BAR_BAZ", to: "/foo_bar_baz.rb" }, + + { from: "foo_bar_baz.rb", to: "foo_bar_baz.rb" }, + { from: "foo/bar/baz.rb", to: "foo/bar/baz.rb" }, + { from: "foo/barBaz.rb", to: "foo/bar_baz.rb" }, + { from: "/foo_bar_baz.rb", to: "/foo_bar_baz.rb" }, + { from: "/foo/bar/baz.rb", to: "/foo/bar/baz.rb" }, + { from: "/foo/barBaz.rb", to: "/foo/bar_baz.rb" } + ] + end + end +end From 35274125de189117ae0dd50b184003852c094612 Mon Sep 17 00:00:00 2001 From: Chris Welham <71787007+apexatoll@users.noreply.github.com> Date: Sat, 16 Sep 2023 11:45:09 +0100 Subject: [PATCH 17/17] Implement constant inflector --- lib/kangaru/inflectors/constant_inflector.rb | 13 ++++++ sig/kangaru/inflectors/constant_inflector.rbs | 7 ++++ .../inflectors/constant_inflector_spec.rb | 42 +++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 lib/kangaru/inflectors/constant_inflector.rb create mode 100644 sig/kangaru/inflectors/constant_inflector.rbs create mode 100644 spec/kangaru/inflectors/constant_inflector_spec.rb diff --git a/lib/kangaru/inflectors/constant_inflector.rb b/lib/kangaru/inflectors/constant_inflector.rb new file mode 100644 index 0000000..c9ec8b1 --- /dev/null +++ b/lib/kangaru/inflectors/constant_inflector.rb @@ -0,0 +1,13 @@ +module Kangaru + module Inflectors + class ConstantInflector < ClassInflector + LAST_WORD = /(::)?(?!.*::)(.*)$/ + + def inflect + super.gsub(LAST_WORD) do |last_word| + ScreamingSnakecaseInflector.inflect(last_word) + end + end + end + end +end diff --git a/sig/kangaru/inflectors/constant_inflector.rbs b/sig/kangaru/inflectors/constant_inflector.rbs new file mode 100644 index 0000000..921d79b --- /dev/null +++ b/sig/kangaru/inflectors/constant_inflector.rbs @@ -0,0 +1,7 @@ +module Kangaru + module Inflectors + class ConstantInflector < ClassInflector + LAST_WORD: Regexp + end + end +end diff --git a/spec/kangaru/inflectors/constant_inflector_spec.rb b/spec/kangaru/inflectors/constant_inflector_spec.rb new file mode 100644 index 0000000..c4d456a --- /dev/null +++ b/spec/kangaru/inflectors/constant_inflector_spec.rb @@ -0,0 +1,42 @@ +RSpec.describe Kangaru::Inflectors::ConstantInflector do + subject(:inflector) { described_class.new(string) } + + describe "#inflect" do + subject(:inflection) { inflector.inflect } + + include_examples :runs_inflections, [ + { from: "foo_bar_baz", to: "FOO_BAR_BAZ" }, + { from: "foo_bar__baz", to: "FOO_BAR_BAZ" }, + { from: "foo__bar_baz", to: "FOO_BAR_BAZ" }, + { from: "foo__bar__baz", to: "FOO_BAR_BAZ" }, + + { from: "foo-bar-baz", to: "FOO_BAR_BAZ" }, + { from: "foo--bar--baz", to: "FOO_BAR_BAZ" }, + { from: "foo-bar--baz", to: "FOO_BAR_BAZ" }, + { from: "foo--bar-baz", to: "FOO_BAR_BAZ" }, + + { from: "fooBarBaz", to: "FOO_BAR_BAZ" }, + { from: "FooBarBaz", to: "FOO_BAR_BAZ" }, + { from: "FOOBARBAZ", to: "FOOBARBAZ" }, + { from: "FOO_BAR_BAZ", to: "FOO_BAR_BAZ" }, + + { from: "Foo::Bar::Baz", to: "Foo::Bar::BAZ" }, + { from: "Foo::BarBaz", to: "Foo::BAR_BAZ" }, + { from: "FooBar::Baz", to: "FooBar::BAZ" }, + + { from: "::FOOBARBAZ", to: "::FOOBARBAZ" }, + { from: "::Foo::Bar::Baz", to: "::Foo::Bar::BAZ" }, + { from: "::Foo::BarBaz", to: "::Foo::BAR_BAZ" }, + { from: "::FooBar::Baz", to: "::FooBar::BAZ" }, + { from: "::FooBarBaz", to: "::FOO_BAR_BAZ" }, + { from: "::FOO_BAR_BAZ", to: "::FOO_BAR_BAZ" }, + + { from: "foo_bar_baz.rb", to: "FOO_BAR_BAZ" }, + { from: "foo/bar/baz.rb", to: "Foo::Bar::BAZ" }, + { from: "foo/barBaz.rb", to: "Foo::BAR_BAZ" }, + { from: "/foo_bar_baz.rb", to: "::FOO_BAR_BAZ" }, + { from: "/foo/bar/baz.rb", to: "::Foo::Bar::BAZ" }, + { from: "/foo/barBaz.rb", to: "::Foo::BAR_BAZ" } + ] + end +end