feature_pack 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 102101eb87f4153fd1db6cdd46f15bf1776912773498a1f585db49e7a6be707d
4
- data.tar.gz: a3492ed0b1add63f95a171964c2e688f9d94d351d18112c2961aabcb1c80b845
3
+ metadata.gz: 7451b49f9dda65ec4aa60a6bb26a976f09258c292221ac884f6a1f2db7dd769b
4
+ data.tar.gz: eccbe2c439b2810c149a174225f9a20821a28f16b0af5876bf9bc32ef22d3f04
5
5
  SHA512:
6
- metadata.gz: d959f7066efab1bb4371914ad61689384d4c6d9a25c4bbc57a0982f14c6cb694c7025aa5e81557790e52327e6112c6ef28cac5e36612abcfaf01f432d5fe01f1
7
- data.tar.gz: f6e1c3c5bd1518732d146567446b4a36b4f4812605425c7ee476c0951685ab97f1ed4f4a87d91defbfb0b876b4428620a7fe28d3737a09b26142b2ad72671c9a
6
+ metadata.gz: eedb788af59f03e15dbc7d54d4da52c40491f9374e32160c40779f8318b084566961005802261b34b87c6c2cf9134b68c6fc21c9c7ed26671894bbd5744bfa44
7
+ data.tar.gz: 9ea9c70925ed3293e7bb1348e7eb3276d0db1ff2129bf17380b1b43769f043b7c1a98cd566605cd8cdad723d0c0efb87d98b2f6536725f07e4c004fd57b07ac5
data/doc/hooks.md ADDED
@@ -0,0 +1,108 @@
1
+ # Sistema de Hooks do FeaturePack
2
+
3
+ ## Hook after_initialize
4
+
5
+ O FeaturePack agora suporta hooks `after_initialize` que permitem executar código customizado após o carregamento de grupos e features.
6
+
7
+ ### Como funciona
8
+
9
+ Durante o processo de setup do FeaturePack, após todos os grupos e features serem descobertos e configurados, o sistema procura e executa arquivos `after_initialize.rb` específicos.
10
+
11
+ ### Localização dos arquivos
12
+
13
+ - **Para grupos**: `app/feature_packs/[nome_do_grupo]/_group_space/after_initialize.rb`
14
+ - **Para features**: `app/feature_packs/[nome_do_grupo]/[nome_da_feature]/after_initialize.rb`
15
+
16
+ ### Contexto de execução
17
+
18
+ Os arquivos `after_initialize.rb` são executados no contexto do objeto group ou feature, permitindo acesso direto a todas as suas propriedades através de `self`.
19
+
20
+ ### Exemplos de uso
21
+
22
+ #### Hook para grupo
23
+
24
+ ```ruby
25
+ # app/feature_packs/group_admin/_group_space/after_initialize.rb
26
+
27
+ # Registrar o grupo em um sistema de auditoria
28
+ Rails.logger.info "Grupo #{name} carregado com #{features.size} features"
29
+
30
+ # Configurar permissões globais do grupo
31
+ features.each do |feature|
32
+ Rails.logger.info " - Feature #{feature.name} disponível em #{feature.manifest[:url]}"
33
+ end
34
+
35
+ # Carregar configurações específicas do grupo
36
+ config_file = File.join(absolute_path, GROUP_SPACE_DIRECTORY, 'config.yml')
37
+ if File.exist?(config_file)
38
+ @config = YAML.load_file(config_file)
39
+ end
40
+ ```
41
+
42
+ #### Hook para feature
43
+
44
+ ```ruby
45
+ # app/feature_packs/group_admin/feature_users/after_initialize.rb
46
+
47
+ # Registrar rotas dinâmicas
48
+ Rails.logger.info "Feature #{name} inicializada no grupo #{group.name}"
49
+
50
+ # Verificar dependências
51
+ required_gems = %w[devise cancancan]
52
+ required_gems.each do |gem_name|
53
+ unless Gem.loaded_specs.key?(gem_name)
54
+ Rails.logger.warn "Feature #{name} requer a gem #{gem_name}"
55
+ end
56
+ end
57
+
58
+ # Registrar a feature em um sistema de métricas
59
+ StatsD.increment("features.#{group.name}.#{name}.loaded")
60
+
61
+ # Configurar cache específico da feature
62
+ Rails.cache.write("feature:#{group.name}:#{name}:loaded_at", Time.current)
63
+ ```
64
+
65
+ ### Propriedades disponíveis
66
+
67
+ #### No contexto de grupo
68
+
69
+ - `name` - Nome do grupo (symbol)
70
+ - `absolute_path` - Caminho absoluto do grupo
71
+ - `relative_path` - Caminho relativo do grupo
72
+ - `features` - Array com todas as features do grupo
73
+ - `manifest` - Hash com dados do manifest.yaml
74
+ - `routes_file` - Caminho do arquivo de rotas
75
+
76
+ #### No contexto de feature
77
+
78
+ - `name` - Nome da feature (symbol)
79
+ - `group` - Referência ao grupo pai
80
+ - `absolute_path` - Caminho absoluto da feature
81
+ - `relative_path` - Caminho relativo da feature
82
+ - `namespace` - Módulo Ruby da feature
83
+ - `manifest` - Hash com dados do manifest.yaml
84
+ - `routes_file` - Caminho do arquivo de rotas
85
+
86
+ ### Casos de uso comuns
87
+
88
+ 1. **Logging e auditoria** - Registrar quando grupos/features são carregados
89
+ 2. **Validação de dependências** - Verificar se gems ou recursos necessários estão disponíveis
90
+ 3. **Configuração dinâmica** - Carregar configurações específicas
91
+ 4. **Registro em sistemas externos** - Integrar com sistemas de métricas ou monitoramento
92
+ 5. **Inicialização de recursos** - Preparar caches, conexões ou outros recursos
93
+ 6. **Verificação de segurança** - Validar permissões ou políticas de acesso
94
+
95
+ ### Boas práticas
96
+
97
+ 1. Mantenha os hooks leves e rápidos - eles são executados durante o boot da aplicação
98
+ 2. Use logging apropriado para facilitar debug
99
+ 3. Trate exceções adequadamente para não quebrar o processo de inicialização
100
+ 4. Evite operações síncronas pesadas (I/O, rede, etc)
101
+ 5. Use o hook apenas para configurações que realmente precisam acontecer após a carga completa
102
+
103
+ ### Ordem de execução
104
+
105
+ 1. Todos os grupos são descobertos e configurados
106
+ 2. Todas as features são descobertas e configuradas
107
+ 3. Hooks `after_initialize` dos grupos são executados
108
+ 4. Hooks `after_initialize` das features são executados (na ordem de descoberta)
data/lib/feature_pack.rb CHANGED
@@ -11,6 +11,7 @@ module FeaturePack
11
11
  GROUP_SPACE_DIRECTORY = '_group_space'.freeze
12
12
  MANIFEST_FILE_NAME = 'manifest.yaml'.freeze
13
13
  CONTROLLER_FILE_NAME = 'controller.rb'.freeze
14
+ AFTER_INITIALIZE_FILE_NAME = '__after_initialize.rb'.freeze
14
15
 
15
16
  # Attribute readers that will be dynamically defined
16
17
  ATTR_READERS = %i[
@@ -39,9 +40,7 @@ module FeaturePack
39
40
  # Finds a group by name
40
41
  # @param group_name [Symbol] The name of the group
41
42
  # @return [OpenStruct, nil] The group object or nil if not found
42
- def group(group_name)
43
- @@groups.find { |g| g.name.eql?(group_name) }
44
- end
43
+ def group(group_name) = @@groups.find { it.name.eql?(group_name) }
45
44
 
46
45
  # Finds a feature within a group
47
46
  # @param group_name [Symbol] The name of the group
@@ -68,9 +67,7 @@ module FeaturePack
68
67
  @@javascript_files_paths = discover_javascript_files
69
68
  end
70
69
 
71
- def load_dependencies
72
- load @@path.join('feature_pack/error.rb')
73
- end
70
+ def load_dependencies = load @@path.join('feature_pack/error.rb')
74
71
 
75
72
  def validate_features_path!
76
73
  raise "Invalid features_path: '#{@@features_path}'" if @@features_path.nil?
@@ -86,14 +83,11 @@ module FeaturePack
86
83
  def finalize_setup
87
84
  ATTR_READERS.each { |attr| define_singleton_method(attr) { class_variable_get("@@#{attr}") } }
88
85
  @@ignored_paths << @@path.join('feature_pack/feature_pack_routes.rb')
86
+ execute_after_initialize_hooks
89
87
  @@setup_executed_flag = true
90
88
  end
91
89
 
92
- def discover_groups
93
- @@groups = Dir.glob("#{@@features_path}/[!_]*/").map do |group_path|
94
- build_group(group_path)
95
- end
96
- end
90
+ def discover_groups = @@groups = Dir.glob("#{@@features_path}/[!_]*/").map { build_group(it) }
97
91
 
98
92
  def build_group(group_path)
99
93
  relative_path = Pathname.new(group_path)
@@ -148,8 +142,8 @@ module FeaturePack
148
142
  end
149
143
 
150
144
  def setup_group_aliases(group)
151
- group.manifest.fetch(:const_aliases, []).each do |alias_data|
152
- alias_method_name, alias_const_name = alias_data.first
145
+ group.manifest.fetch(:const_aliases, []).each do
146
+ alias_method_name, alias_const_name = it.first
153
147
  group.define_singleton_method(alias_method_name) do
154
148
  "FeaturePack::#{group.name.to_s.camelize}::#{alias_const_name}".constantize
155
149
  end
@@ -157,21 +151,10 @@ module FeaturePack
157
151
  end
158
152
 
159
153
  def define_group_methods(group)
160
- def group.feature(feature_name)
161
- features.find { |p| p.name.eql?(feature_name) }
162
- end
163
-
164
- def group.views_path
165
- "#{base_dir}/#{GROUP_SPACE_DIRECTORY}/views"
166
- end
167
-
168
- def group.view(view_name)
169
- "#{base_dir}/#{GROUP_SPACE_DIRECTORY}/views/#{view_name}"
170
- end
171
-
172
- def group.javascript_module(javascript_file_name)
173
- "#{base_dir}/#{GROUP_SPACE_DIRECTORY}/javascript/#{javascript_file_name}"
174
- end
154
+ def group.feature(feature_name) = features.find { it.name.eql?(feature_name) }
155
+ def group.views_path = "#{base_dir}/#{GROUP_SPACE_DIRECTORY}/views"
156
+ def group.view(view_name) = "#{base_dir}/#{GROUP_SPACE_DIRECTORY}/views/#{view_name}"
157
+ def group.javascript_module(javascript_file_name) = "#{base_dir}/#{GROUP_SPACE_DIRECTORY}/javascript/#{javascript_file_name}"
175
158
  end
176
159
 
177
160
  def discover_features
@@ -205,6 +188,20 @@ module FeaturePack
205
188
  group.features << feature
206
189
  end
207
190
 
191
+ def execute_after_initialize_hooks
192
+ # Executar hooks dos grupos
193
+ @@groups.each do |group|
194
+ hook_file = File.join(group.metadata_path, AFTER_INITIALIZE_FILE_NAME)
195
+ group.instance_eval(File.read(hook_file), hook_file) if File.exist?(hook_file)
196
+
197
+ # Executar hooks das features
198
+ group.features.each do |feature|
199
+ hook_file = File.join(feature.absolute_path, AFTER_INITIALIZE_FILE_NAME)
200
+ feature.instance_eval(File.read(hook_file), hook_file) if File.exist?(hook_file)
201
+ end
202
+ end
203
+ end
204
+
208
205
  def validate_feature_id!(base_path, relative_path)
209
206
  if base_path.scan(FEATURE_ID_PATTERN).empty?
210
207
  raise "Feature '#{relative_path}' does not have a valid ID. Expected format: feature_<id>_<name>"
@@ -212,6 +209,9 @@ module FeaturePack
212
209
  end
213
210
 
214
211
  def setup_feature_paths(relative_path, routes_file_path)
212
+ # Handled after initialize hooks
213
+ @@ignored_paths << File.join(relative_path, AFTER_INITIALIZE_FILE_NAME)
214
+
215
215
  # Custom routes file loads before Rails default routes
216
216
  @@ignored_paths << routes_file_path
217
217
 
@@ -246,26 +246,15 @@ module FeaturePack
246
246
  end
247
247
 
248
248
  def define_feature_methods(feature)
249
- def feature.class_name
250
- "FeaturePack::#{group.name.to_s.camelize}::#{name.to_s.camelize}"
251
- end
252
-
253
- def feature.namespace
254
- class_name.constantize
255
- end
256
-
257
- def feature.view(view_name)
258
- "#{views_relative_path}/#{view_name}"
259
- end
260
-
261
- def feature.javascript_module(javascript_file_name)
262
- "#{javascript_relative_path}/#{javascript_file_name}"
263
- end
249
+ def feature.class_name = "FeaturePack::#{group.name.to_s.camelize}::#{name.to_s.camelize}"
250
+ def feature.namespace = class_name.constantize
251
+ def feature.view(view_name) = "#{views_relative_path}/#{view_name}"
252
+ def feature.javascript_module(javascript_file_name) = "#{javascript_relative_path}/#{javascript_file_name}"
264
253
  end
265
254
 
266
255
  def setup_feature_aliases(feature)
267
- feature.manifest.fetch(:const_aliases, []).each do |alias_data|
268
- alias_method_name, alias_const_name = alias_data.first
256
+ feature.manifest.fetch(:const_aliases, []).each do
257
+ alias_method_name, alias_const_name = it.first
269
258
  feature.define_singleton_method(alias_method_name) do
270
259
  "#{class_name}::#{alias_const_name}".constantize
271
260
  end
@@ -0,0 +1,11 @@
1
+ # Hook executado após a inicialização da feature <%= @feature_class_name %>
2
+ # Este arquivo é executado no contexto do objeto feature
3
+ # Você tem acesso a todas as propriedades da feature através de 'self'
4
+ #
5
+ # Exemplo de uso:
6
+ # puts "Feature #{name} foi inicializada!"
7
+ # puts "Grupo da feature: #{group.name}"
8
+ # puts "Caminho absoluto: #{absolute_path}"
9
+ # puts "Namespace: #{namespace}"
10
+ #
11
+ # Adicione seu código customizado abaixo:
@@ -1,5 +1,5 @@
1
1
  # Feature controller for <%= @feature_class_name %>
2
- class FeaturePack::<%= @group_class_name %>::<%= @feature_class_name %>Controller < FeaturePack::<%= @group_class_name %>Controller
2
+ class FeaturePack::<%= @group_class_name %>::<%= @feature_class_name %>Controller < FeaturePack::Controller
3
3
  # The 'index' action is already defined in the parent controller
4
4
  # Add your feature-specific actions and logic here
5
5
 
@@ -0,0 +1,10 @@
1
+ # Hook executado após a inicialização do grupo <%= @class_name %>
2
+ # Este arquivo é executado no contexto do objeto group
3
+ # Você tem acesso a todas as propriedades do grupo através de 'self'
4
+ #
5
+ # Exemplo de uso:
6
+ # puts "Grupo #{name} foi inicializado!"
7
+ # puts "Caminho do grupo: #{absolute_path}"
8
+ # puts "Features do grupo: #{features.map(&:name).join(', ')}"
9
+ #
10
+ # Adicione seu código customizado abaixo:
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feature_pack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gedean Dias
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-02-09 00:00:00.000000000 Z
10
+ date: 2025-08-03 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -40,6 +40,7 @@ extra_rdoc_files: []
40
40
  files:
41
41
  - README.md
42
42
  - doc/feature_pack.md
43
+ - doc/hooks.md
43
44
  - lib/feature_pack.rb
44
45
  - lib/feature_pack/api/controller.rb
45
46
  - lib/feature_pack/controller.rb
@@ -48,6 +49,7 @@ files:
48
49
  - lib/feature_pack/group_controller.rb
49
50
  - lib/generators/feature_pack/add_feature/USAGE
50
51
  - lib/generators/feature_pack/add_feature/add_feature_generator.rb
52
+ - lib/generators/feature_pack/add_feature/templates/after_initialize.rb.tt
51
53
  - lib/generators/feature_pack/add_feature/templates/controller.rb.tt
52
54
  - lib/generators/feature_pack/add_feature/templates/doc/readme.md.tt
53
55
  - lib/generators/feature_pack/add_feature/templates/manifest.yaml.tt
@@ -57,6 +59,7 @@ files:
57
59
  - lib/generators/feature_pack/add_feature/templates/views/partials/_header.html.slim.tt
58
60
  - lib/generators/feature_pack/add_group/USAGE
59
61
  - lib/generators/feature_pack/add_group/add_group_generator.rb
62
+ - lib/generators/feature_pack/add_group/templates/_group_space/after_initialize.rb.tt
60
63
  - lib/generators/feature_pack/add_group/templates/_group_space/controller.rb.tt
61
64
  - lib/generators/feature_pack/add_group/templates/_group_space/manifest.yaml.tt
62
65
  - lib/generators/feature_pack/add_group/templates/_group_space/routes.rb.tt
@@ -85,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
88
  - !ruby/object:Gem::Version
86
89
  version: '0'
87
90
  requirements: []
88
- rubygems_version: 3.6.9
91
+ rubygems_version: 3.7.1
89
92
  specification_version: 4
90
93
  summary: A different approach to organizing Rails app features.
91
94
  test_files: []