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 +7 -0
- data/README.md +2 -0
- data/lib/feature_pack/api/controller.rb +3 -0
- data/lib/feature_pack/controller.rb +44 -0
- data/lib/feature_pack/feature_pack_routes.rb +22 -0
- data/lib/feature_pack/group_controller.rb +33 -0
- data/lib/feature_pack.rb +115 -0
- metadata +65 -0
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,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
|
data/lib/feature_pack.rb
ADDED
@@ -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: []
|