feature_pack 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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: []