Skip to content

Commit 48bfa6c

Browse files
author
Сатаров Юрий Сергеевич
committed
Merge branch 'feat/DEX-3358/add-regexp-generator' into 'master'
[DEX-3358] feat: add regexp consumer rails generator Closes DEX-3358 See merge request nstmrt/rubygems/sbmt-kafka_consumer!90
2 parents 7ba3264 + be27f96 commit 48bfa6c

11 files changed

Lines changed: 398 additions & 5 deletions

File tree

.rubocop.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ RSpec/VerifiedDoubles:
2828
Exclude:
2929
- spec/**/*_spec.rb
3030

31+
RSpec/SpecFilePathFormat:
32+
Exclude:
33+
- spec/generators/**/*
34+
35+
RSpec/InstanceVariable:
36+
Exclude:
37+
- spec/generators/**/*
38+
3139
Style/SingleLineMethods:
3240
Enabled: false
3341

Appraisals

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# See compatibility table at https://www.fastruby.io/blog/ruby/rails/versions/compatibility-table.html
44

55
versions_map = {
6-
"6.1" => %w[2.7 3.0],
6+
"6.1" => %w[3.0],
77
"7.0" => %w[3.1],
88
"7.1" => %w[3.2],
99
"7.2" => %w[3.3],

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1313

1414
### Fixed
1515

16+
## [3.8.0] - 2026-03-03
17+
18+
### Added
19+
20+
- `kafka_consumer:regexp_consumer` generator for injecting a regexp-based consumer group entry into `config/kafka_consumer.yml`; offers to run `kafka_consumer:install` if the config file is missing
21+
1622
## [3.7.1] - 2026-02-17
1723

1824
### Fixed

Gemfile

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,3 @@
33
source ENV.fetch("RUBYGEMS_PUBLIC_SOURCE", "https://rubygems.org/")
44

55
gemspec
6-
7-
# FIXME: remove this after drop support for Ruby 2.7
8-
gem "ffi", "< 1.17"

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,50 @@ To generate an Inbox consumer for use with gem [sbmt-outbox](https://github.com/
5353
rails g kafka_consumer:inbox_consumer MaybeNamespaced::Name some-consumer-group some-topic
5454
```
5555

56+
### Regexp consumer
57+
58+
To generate a regexp-topic consumer:
59+
60+
```shell
61+
rails g kafka_consumer:regexp_consumer GROUP_KEY CONSUMER_KLASS TOPIC_REGEXP --env-prefix ENV_PREFIX
62+
```
63+
64+
Where:
65+
- `GROUP_KEY` — YAML key for the consumer group (e.g. `order`)
66+
- `CONSUMER_KLASS` — consumer class name (e.g. `MyApp::OrderConsumer`)
67+
- `TOPIC_REGEXP` — topic regexp (e.g. `'my\.app\.order(\.\d+)?$'`)
68+
- `--env-prefix` (required) — prefix for the `ENV_PREFIX_CONSUMER_GROUP_NAME` and `ENV_PREFIX_TOPIC_REGEXP` environment variables
69+
- `--init-attrs key:value ...` — optional consumer `init_attrs` as `key:value` pairs; boolean values (`true`/`false`) are emitted unquoted
70+
- `--deserializer-klass` — optional deserializer class; omit to skip the `deserializer:` section
71+
72+
If `config/kafka_consumer.yml` does not exist, the generator will offer to run `kafka_consumer:install` first.
73+
74+
Examples:
75+
76+
```shell
77+
# Consumer with init_attrs
78+
rails g kafka_consumer:regexp_consumer order \
79+
"MyApp::OrderConsumer" \
80+
'my\.app\.order(\.\d+)?$' \
81+
--env-prefix MY_APP_ORDER \
82+
--init-attrs inbox_item:OrderInboxItem
83+
84+
# Consumer with deserializer
85+
rails g kafka_consumer:regexp_consumer events \
86+
"MyApp::EventsConsumer" \
87+
'my\.app\.events(\.\d+)?$' \
88+
--env-prefix MY_APP_EVENTS \
89+
--deserializer-klass "Sbmt::KafkaConsumer::Serialization::JsonDeserializer"
90+
91+
# Consumer with both
92+
rails g kafka_consumer:regexp_consumer order_init \
93+
"MyApp::OrderInitConsumer" \
94+
'my\.app\.order\.init(\.\d+)?$' \
95+
--env-prefix MY_APP_ORDER_INIT \
96+
--init-attrs envelope_schema:my.envelope data_schema:my.app.order.init skip_on_error:true \
97+
--deserializer-klass "Sbmt::KafkaConsumer::Serialization::JsonDeserializer"
98+
```
99+
56100
## Manual configuration
57101

58102
The `config/kafka_consumer.yml` file is a main configuration for the gem.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Description:
2+
Injects a regexp-based consumer group entry into config/kafka_consumer.yml.
3+
If kafka_consumer.yml does not exist, offers to run kafka_consumer:install first.
4+
5+
Required positional args: GROUP_KEY, CONSUMER_KLASS, TOPIC_REGEXP
6+
Required option: --env-prefix
7+
8+
Example:
9+
# Inbox consumer with init_attrs (no deserializer)
10+
bin/rails generate kafka_consumer:regexp_consumer order \
11+
"Ecom::EventStreaming::Kafka::RegularEventsInboxConsumer" \
12+
'seeker\.paas-stand\.event-streaming\.order(\.\d+)?$' \
13+
--env-prefix ES_ORDER \
14+
--init-attrs inbox_item:EsOrderInboxItem
15+
16+
# Consumer with deserializer (no init_attrs)
17+
bin/rails generate kafka_consumer:regexp_consumer init_exports \
18+
"Ecom::EventStreaming::Kafka::ControlRequestsConsumer" \
19+
'yc\.seeker\.paas-stand\.event-streaming\.control\.in(\.\d+)?' \
20+
--env-prefix ES_CONTROL_REQUESTS \
21+
--deserializer-klass "Sbmt::KafkaConsumer::Serialization::JsonDeserializer"
22+
23+
# Consumer with both init_attrs (including boolean) and deserializer
24+
bin/rails generate kafka_consumer:regexp_consumer seeker_init_exports \
25+
"SeekerInitEventsConsumer" \
26+
'seeker\.paas-stand\.event-streaming\.order\.init(\.\d+)?$' \
27+
--env-prefix ES_SEEKER_INIT_EXPORTS \
28+
--init-attrs envelope_schema:cloud-events.init.envelope data_schema:seeker.paas-stand.event-streaming.order.init skip_on_error:true \
29+
--deserializer-klass "Sbmt::KafkaConsumer::Serialization::JsonDeserializer"
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# frozen_string_literal: true
2+
3+
require "rails/generators/named_base"
4+
require "generators/kafka_consumer/concerns/configuration"
5+
6+
module KafkaConsumer
7+
module Generators
8+
class RegexpConsumerGenerator < Rails::Generators::NamedBase
9+
include Concerns::Configuration
10+
11+
source_root File.expand_path("templates", __dir__)
12+
13+
desc "Injects a regexp-based consumer group entry into config/kafka_consumer.yml"
14+
15+
argument :consumer_klass, type: :string,
16+
desc: "Consumer class name (e.g. Ecom::EventStreaming::Kafka::RegularEventsInboxConsumer)"
17+
18+
argument :topic_regexp, type: :string,
19+
desc: "Topic regexp value (e.g. 'seeker\\.paas-stand\\.event-streaming\\.order(\\.\\d+)?\\$')"
20+
21+
class_option :env_prefix, type: :string, required: true,
22+
desc: "ENV variable prefix for CONSUMER_GROUP_NAME and TOPIC_REGEXP (e.g. ES_ORDER)"
23+
24+
class_option :init_attrs, type: :hash, default: {},
25+
banner: "key:value key:value",
26+
desc: "Consumer init_attrs as key:value pairs (e.g. inbox_item:Foo skip_on_error:true)"
27+
28+
class_option :deserializer_klass, type: :string, default: nil,
29+
desc: "Deserializer class (omit to skip deserializer section)"
30+
31+
def inject_consumer_group
32+
check_config_file!
33+
insert_into_file CONFIG_PATH, consumer_group_entry, after: "consumer_groups:\n"
34+
end
35+
36+
private
37+
38+
# Override to resolve path relative to destination_root (works in both
39+
# app context and generator tests with a tmpdir destination_root).
40+
def check_config_file!
41+
return if File.exist?(File.join(destination_root, CONFIG_PATH))
42+
43+
generator_name = "kafka_consumer:install"
44+
answer = ask("The file #{CONFIG_PATH} does not appear to exist. Would you like to generate it? [Yn]")
45+
if (answer.presence || "y").casecmp("y").zero?
46+
generate generator_name
47+
else
48+
raise Rails::Generators::Error,
49+
"Please generate #{CONFIG_PATH} by running `bin/rails g #{generator_name}` or add this file manually."
50+
end
51+
end
52+
53+
def group_key
54+
file_name
55+
end
56+
57+
def env_prefix
58+
options[:env_prefix]
59+
end
60+
61+
def init_attrs
62+
options[:init_attrs]
63+
end
64+
65+
def deserializer_klass
66+
options[:deserializer_klass]
67+
end
68+
69+
def format_attr_value(value)
70+
(value == "true" || value == "false") ? value : "\"#{value}\""
71+
end
72+
73+
def consumer_group_entry
74+
template_path = File.join(self.class.source_root, "consumer_group.yml.erb")
75+
ERB.new(File.read(template_path), trim_mode: "%-").result(binding)
76+
end
77+
end
78+
end
79+
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'<%= group_key %>':
2+
name: <%%= ENV.fetch("<%= env_prefix %>_CONSUMER_GROUP_NAME", "<%= group_key %>") %><%%= ENV.fetch("CONSUMER_GROUP_SUFFIX", "") %>
3+
topics:
4+
- regexp: <%%= ENV.fetch("<%= env_prefix %>_TOPIC_REGEXP", '<%= topic_regexp %>') %>
5+
consumer:
6+
klass: "<%= consumer_klass %>"
7+
<%- unless init_attrs.empty? -%>
8+
init_attrs:
9+
<%- init_attrs.each do |key, value| -%>
10+
<%= key %>: <%= format_attr_value(value) %>
11+
<%- end -%>
12+
<%- end -%>
13+
<%- if deserializer_klass -%>
14+
deserializer:
15+
klass: "<%= deserializer_klass %>"
16+
<%- end -%>

lib/sbmt/kafka_consumer/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
module Sbmt
44
module KafkaConsumer
5-
VERSION = "3.7.1"
5+
VERSION = "3.8.0"
66
end
77
end

0 commit comments

Comments
 (0)