feature_pack 0.0.5

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bdafad74607b3a61e1783639b1a516875eeb6744e2e6c24a334ad04930036dcb
4
+ data.tar.gz: f63ce1ad345185c69e009e507663f082f47c4799d1c286178b9a22ec69e2db06
5
+ SHA512:
6
+ metadata.gz: 445dbeb6ea88aeab0136cba9c4425c8aefb78126eca729fee511b2503016289e6b65d0c99490017aa971adc411ba0c5ef8473dca944b110c2975c290e5281caf
7
+ data.tar.gz: 14598e24f1198f63291fe5cc339ecf659aa51510dc6a34b8f61e9858a398f85a5acbe44030a76335c2b1192a40777fe3a8cfb57f1b06f5c64d7dcaa9611dfb82
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # Feature Pack
2
+ Organizes and sets up the architecture of micro-applications within a Rails application, enabling the segregation of code, management, and isolation of functionalities, which can be developed, tested, and maintained independently of each other.
@@ -0,0 +1,3 @@
1
+ class FeaturePack::API::Controller < ActionController::API
2
+
3
+ end
@@ -0,0 +1,44 @@
1
+ class FeaturePack::Controller < ApplicationController
2
+ before_action :set_feature
3
+ before_action :set_view_lookup_context_prefix
4
+ before_action :set_layout_paths
5
+
6
+ def home; end
7
+
8
+ private
9
+
10
+ def set_feature
11
+ @feature = FeaturePack.feature *params['controller'].delete_prefix('feature_pack/').split('/').map(&:to_sym)
12
+ end
13
+
14
+ def set_view_lookup_context_prefix
15
+ unless lookup_context.prefixes.include?(@feature.views_relative_path)
16
+ lookup_context.prefixes.prepend(@feature.views_relative_path)
17
+ end
18
+ end
19
+
20
+ def set_layout_paths
21
+ =begin
22
+ Header/Footer Lookup order
23
+
24
+ - Feature dir/_partials, if not exists
25
+ - Fallback to Group, if not exists
26
+ - Fallback to Application's default header/footer
27
+ =end
28
+
29
+ feature_partials_path = @feature.views_relative_path.join('partials')
30
+ group_partials_path = @feature.group.views_path.concat('/partials')
31
+
32
+ if template_exists?('header', feature_partials_path, true)
33
+ @header_layout_path = @feature.view('partials/header')
34
+ elsif template_exists?('header', group_partials_path, true)
35
+ @header_layout_path = @feature.group.view('partials/header')
36
+ end
37
+
38
+ if template_exists?('footer', feature_partials_path, true)
39
+ @footer_layout_path = @feature.view('partials/footer')
40
+ elsif template_exists?('footer', group_partials_path, true)
41
+ @footer_layout_path = @feature.group.view('partials/footer')
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ FeaturePack.groups.each do |group|
2
+ unless group.manifest[:namespace_only]
3
+ # Default "home" route every group has to have.
4
+ get group.manifest[:url],
5
+ to: "#{group.name.name}#home",
6
+ as: group.name.name
7
+
8
+ unless group.routes_file.nil?
9
+ scope group.manifest[:url] do
10
+ draw(group.routes_file)
11
+ end
12
+ end
13
+ end
14
+
15
+ namespace group.name, path: group.manifest[:url] do
16
+ group.features.each do |feature|
17
+ scope feature.manifest[:url], as: feature.name.name do
18
+ draw(feature.routes_file)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,33 @@
1
+ class FeaturePack::GroupController < ApplicationController
2
+ before_action :set_group
3
+ before_action :set_view_lookup_context_prefix
4
+ before_action :set_layout_paths
5
+
6
+ def home
7
+ end
8
+
9
+ private
10
+
11
+ def set_group
12
+ group_name = params[:controller].split('/')[1].to_sym
13
+ @group = FeaturePack.group(group_name)
14
+ end
15
+
16
+ def set_view_lookup_context_prefix
17
+ unless lookup_context.prefixes.include?(@group.views_path)
18
+ lookup_context.prefixes.prepend(@group.views_path)
19
+ end
20
+ end
21
+
22
+ def set_layout_paths
23
+ patials_path = @group.views_path.concat('/partials')
24
+
25
+ if template_exists?('header', patials_path, true)
26
+ @header_layout_path = @group.view('partials/header')
27
+ end
28
+
29
+ if template_exists?('footer', patials_path, true)
30
+ @footer_layout_path = @group.view('partials/footer')
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,115 @@
1
+ require 'active_support/all'
2
+
3
+ module FeaturePack
4
+ GROUP_ID_PATTERN = /^group_.*?_/.freeze
5
+ FEATURE_ID_PATTERN = /^feature_.*?_/.freeze
6
+ RELATIVE_ROOT_PATH = 'app/feature_packs'.freeze
7
+ GROUP_METADATA_DIRECTORY = '_group_metadata'.freeze
8
+ MANIFEST_FILE_NAME = 'manifest.yml'.freeze
9
+ CONTROLLER_FILE_NAME = 'controller.rb'.freeze
10
+
11
+ ATTR_READERS = %i[core_path absolute_root_path relative_root_path
12
+ groups ignored_paths custom_layouts_paths javascript_files_paths
13
+ group_controllers_paths controllers_paths].freeze
14
+
15
+ def self.setup
16
+ raise 'FeaturePack already setup!' if defined?(@@relative_root_path)
17
+
18
+ @@core_path = Pathname.new(__dir__)
19
+
20
+ @@group_controllers_paths = []
21
+ @@controllers_paths = []
22
+ @@relative_root_path = Pathname.new(RELATIVE_ROOT_PATH)
23
+ @@absolute_root_path = Rails.root.join(RELATIVE_ROOT_PATH)
24
+ @@ignored_paths = Dir.glob("#{RELATIVE_ROOT_PATH}/[!]*/")
25
+ @@javascript_files_paths = Dir.glob("#{@@relative_root_path}/[!_]*/**/*.js")
26
+ .map { |js_path| js_path.sub(/^#{Regexp.escape(@@relative_root_path.to_s)}\//, '') }.to_a
27
+
28
+ @@custom_layouts_paths = Dir.glob("#{@@relative_root_path}/[!_]*/**/views/layouts")
29
+ .map { |layout_path| layout_path.delete_suffix '/layouts' }
30
+
31
+ ATTR_READERS.each { |attr| define_singleton_method(attr) { class_variable_get("@@#{attr}") } }
32
+
33
+ # load @@core_path.join('feature_pack/error.rb')
34
+ @@ignored_paths << @@core_path.join('feature_pack/feature_pack_routes.rb')
35
+
36
+ @@groups = Dir.glob("#{RELATIVE_ROOT_PATH}/[!_]*/").map do |group_path|
37
+ relative_path = Pathname.new(group_path)
38
+ base_path = File.basename(group_path, File::SEPARATOR)
39
+
40
+ # On route draw call, the extension is ignored
41
+ routes_file = File.exist?(File.join(group_path, GROUP_METADATA_DIRECTORY, 'routes.rb')) ? File.join(base_path, GROUP_METADATA_DIRECTORY, 'routes') : nil
42
+
43
+ @@group_controllers_paths << File.join(group_path, GROUP_METADATA_DIRECTORY, CONTROLLER_FILE_NAME)
44
+
45
+ raise "Group '#{base_path}' does not have a valid ID" if base_path.scan(GROUP_ID_PATTERN).empty?
46
+ group = OpenStruct.new(
47
+ id: base_path.scan(GROUP_ID_PATTERN).first.delete_suffix('_'),
48
+ name: base_path.gsub(GROUP_ID_PATTERN, '').to_sym,
49
+ metadata_path: Rails.root.join(group_path, GROUP_METADATA_DIRECTORY),
50
+ relative_path: relative_path,
51
+ base_dir: File.basename(relative_path, File::SEPARATOR),
52
+ routes_file: routes_file,
53
+ features: [],
54
+ manifest: YAML.load_file(File.join(group_path, GROUP_METADATA_DIRECTORY, MANIFEST_FILE_NAME)).deep_symbolize_keys
55
+ )
56
+
57
+ def group.feature(feature_name) = features.find { |p| p.name.eql?(feature_name) }
58
+ def group.views_path = "#{base_dir}/#{GROUP_METADATA_DIRECTORY}/views"
59
+ def group.view(view_name) = "#{base_dir}/#{GROUP_METADATA_DIRECTORY}/views/#{view_name}"
60
+
61
+ group
62
+ end
63
+
64
+ @@groups.each do |group|
65
+ Dir.glob("#{group.relative_path}[!_]*/").each do |feature_path|
66
+ absolute_path = Rails.root.join(feature_path)
67
+ relative_path = Pathname.new(feature_path)
68
+ base_path = File.basename(feature_path, File::SEPARATOR)
69
+
70
+ feature_name = base_path.gsub(FEATURE_ID_PATTERN, '').to_sym
71
+ feature_class_name = "#{group.name.name.camelize}::#{feature_name.name.camelize}"
72
+ # FIX-ME
73
+ # params_class_name = "#{feature_pack_class_name}::Params"
74
+
75
+ routes_file_path = relative_path.join('routes.rb')
76
+
77
+ # The custom routes file loads before the Rails default routes, leading to errors like NoMethodError for 'scope'. Ignoring them is required to prevent these issues.
78
+ @@ignored_paths << routes_file_path
79
+
80
+ # Due to Zeiwerk rules, Controllers have special load process
81
+ @@controllers_paths << relative_path.join(CONTROLLER_FILE_NAME)
82
+ @@ignored_paths << relative_path.join(CONTROLLER_FILE_NAME)
83
+
84
+ raise "Resource '#{relative_path}' does not have a valid ID" if base_path.scan(FEATURE_ID_PATTERN).empty?
85
+ feature_sub_path = relative_path.sub(/^#{Regexp.escape(@@relative_root_path.to_s)}\//, '')
86
+ feature = OpenStruct.new(
87
+ id: base_path.scan(FEATURE_ID_PATTERN).first.delete_suffix('_'),
88
+ name: feature_name,
89
+ group: group,
90
+ absolute_path: absolute_path,
91
+ relative_path: relative_path,
92
+ sub_path: feature_sub_path,
93
+ routes_file_path: routes_file_path,
94
+ routes_file: feature_sub_path.join('routes'),
95
+ # controller_path: relative_path.join('controller'),
96
+ views_absolute_path: absolute_path.join('views'),
97
+ views_relative_path: relative_path.sub(/^#{Regexp.escape(@@relative_root_path.to_s)}\//, '').join('views'),
98
+ class_name: feature_class_name,
99
+ # FIX-ME
100
+ #params_class_name: params_class_name,
101
+ manifest: YAML.load_file(File.join(feature_path, MANIFEST_FILE_NAME)).deep_symbolize_keys
102
+ )
103
+
104
+ # FIX-ME
105
+ # def feature.params_class = params_class_name.constantize
106
+ def feature.view(view_name) = "#{views_relative_path}/#{view_name}"
107
+
108
+ group.features << feature
109
+ end
110
+ end
111
+ end
112
+
113
+ def self.group(group_name) = @@groups.find { |g| g.name.eql?(group_name) }
114
+ def self.feature(group_name, feature_name) = group(group_name).feature(feature_name)
115
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: feature_pack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Gedean Dias
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-04-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Organizes and sets up the architecture of micro-applications within a
28
+ Rails application, enabling the segregation of code, management, and isolation of
29
+ functionalities, which can be developed, tested, and maintained independently of
30
+ each other.
31
+ email: gedean.dias@gmail.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - README.md
37
+ - lib/feature_pack.rb
38
+ - lib/feature_pack/api/controller.rb
39
+ - lib/feature_pack/controller.rb
40
+ - lib/feature_pack/feature_pack_routes.rb
41
+ - lib/feature_pack/group_controller.rb
42
+ homepage: https://github.com/gedean/feature_pack
43
+ licenses:
44
+ - MIT
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubygems_version: 3.5.9
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: New way to organize app features in Rails.
65
+ test_files: []