Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement inflectors #1

Merged
merged 17 commits into from
Sep 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions lib/kangaru/inflectors/class_inflector.rb
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions lib/kangaru/inflectors/constant_inflector.rb
Original file line number Diff line number Diff line change
@@ -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
82 changes: 82 additions & 0 deletions lib/kangaru/inflectors/inflector.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
module Kangaru
module Inflectors
class Inflector
extend InflectorMacros

DEFAULT_GROUP_JOINER = "/".freeze

attr_reader :string

def initialize(string)
@string = filter_input(string)
end

def inflect
join_groups(
transform_and_join_tokens(tokeniser.split)
)
end

def self.inflect(string)
new(string).inflect
end

private

def tokeniser
@tokeniser ||= Tokeniser.new(string)
end

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

def token_joiner
class_attribute(:token_joiner)
end

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(
tokens.map { |token| transform_token(token) }
)
end
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

def join_tokens(tokens)
tokens.join(token_joiner)
end

def join_groups(words)
words.join(group_joiner)
end
end
end
end
29 changes: 29 additions & 0 deletions lib/kangaru/inflectors/inflector_macros.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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

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
21 changes: 21 additions & 0 deletions lib/kangaru/inflectors/path_inflector.rb
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions lib/kangaru/inflectors/screaming_snakecase_inflector.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Kangaru
module Inflectors
class ScreamingSnakecaseInflector < SnakecaseInflector
join_groups_with "::"

def inflect
super.upcase
end
end
end
end
11 changes: 11 additions & 0 deletions lib/kangaru/inflectors/snakecase_inflector.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Kangaru
module Inflectors
class SnakecaseInflector < Inflector
transform_tokens_with :downcase

join_tokens_with "_"

join_groups_with "/"
end
end
end
20 changes: 20 additions & 0 deletions lib/kangaru/inflectors/tokeniser.rb
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions sig/kangaru/inflectors/class_inflector.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Kangaru
module Inflectors
class ClassInflector < Inflector
end
end
end
7 changes: 7 additions & 0 deletions sig/kangaru/inflectors/constant_inflector.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Kangaru
module Inflectors
class ConstantInflector < ClassInflector
LAST_WORD: Regexp
end
end
end
43 changes: 43 additions & 0 deletions sig/kangaru/inflectors/inflector.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module Kangaru
module Inflectors
class Inflector
type token_groups = Array[Array[String]]

extend InflectorMacros

DEFAULT_GROUP_JOINER: String

attr_reader string: String

def initialize: (String) -> void

def inflect: -> String

def self.inflect: (String) -> String

private

attr_reader tokeniser: Tokeniser

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

def join_tokens: (Array[String]) -> String

def join_groups: (Array[String]) -> String
end
end
end
20 changes: 20 additions & 0 deletions sig/kangaru/inflectors/inflector_macros.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Kangaru
module Inflectors
module InflectorMacros
@input_filter: untyped
@token_transformer: untyped
@token_joiner: untyped
@group_joiner: untyped

def inherited: (Class) -> void

def filter_input_with: (Regexp) -> void

def transform_tokens_with: (?Symbol?) ?{ (String) -> String } -> void

def join_tokens_with: (String) -> void

def join_groups_with: (String) -> void
end
end
end
7 changes: 7 additions & 0 deletions sig/kangaru/inflectors/path_inflector.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Kangaru
module Inflectors
class PathInflector < Inflector
def inflect: (?with_ext: String?) -> String
end
end
end
6 changes: 6 additions & 0 deletions sig/kangaru/inflectors/screaming_snakecase_inflector.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Kangaru
module Inflectors
class ScreamingSnakecaseInflector < SnakecaseInflector
end
end
end
6 changes: 6 additions & 0 deletions sig/kangaru/inflectors/snakecase_inflector.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Kangaru
module Inflectors
class SnakecaseInflector < Inflector
end
end
end
14 changes: 14 additions & 0 deletions sig/kangaru/inflectors/tokeniser.rbs
Original file line number Diff line number Diff line change
@@ -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
42 changes: 42 additions & 0 deletions spec/kangaru/inflectors/class_inflector_spec.rb
Original file line number Diff line number Diff line change
@@ -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
Loading