lennarb 0.1.7 → 0.2.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.
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
5
-
6
- # Internal dependencies
7
- #
8
- require 'lenna/middleware/default/error_handler'
9
- require 'lenna/middleware/default/logging'
10
- require 'lenna/router'
11
-
12
- # The Lenna module is used to namespace the framework.
13
- #
14
- # @public
15
- #
16
- module Lenna
17
- # The base class is used to start the server.
18
- #
19
- # @public
20
- #
21
- class Application < Router
22
- # Initialize the base class
23
- #
24
- # @yield { ... } the block to be evaluated in the context of the instance.
25
- #
26
- # @return [void | Application] Returns the instance if a block is given.
27
- #
28
- def initialize
29
- super
30
- yield self if block_given?
31
- end
32
- end
33
-
34
- # The base module is used to include the base class.
35
- #
36
- # @public
37
- #
38
- module Base
39
- def self.included(base)
40
- base.extend(ClassMethods)
41
- end
42
-
43
- module ClassMethods
44
- # Initialize the base module
45
- #
46
- # @return [Lenna::Application] Returns the instance.
47
- #
48
- # @public
49
- #
50
- def app = @app ||= Lenna::Application.new
51
- end
52
- end
53
- end
data/lib/lenna/cli/app.rb DELETED
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
5
-
6
- module Lenna
7
- module Cli
8
- # Mediator class for CLI
9
- #
10
- # @private `Since v0.1.0`
11
- #
12
- module App
13
- extend self
14
- # Execute the command
15
- #
16
- # @return [void]
17
- #
18
- def run!(args)
19
- subcommand = args.shift
20
-
21
- strategy = parse_options(subcommand, args)
22
-
23
- strategy.is_a?(Lenna::Cli::Commands::Interface) or fail ::ArgumentError
24
-
25
- strategy.call
26
- end
27
-
28
- private
29
-
30
- def parse_options(command, args)
31
- case command
32
- in 'new' | 'start' then Lenna::Cli::Commands::CreateProject.new(args)
33
- in 'server' | 's' then Lenna::Cli::Commands::StartServer.new(args)
34
- else 'help'
35
- end
36
- end
37
- end
38
- end
39
- end
@@ -1,125 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
5
-
6
- require 'console'
7
- require 'erb'
8
- require 'fileutils'
9
-
10
- module Lenna
11
- module Cli
12
- module Commands
13
- # Command for creating a new app
14
- #
15
- # @private `Since v0.1.0`
16
- #
17
- class CreateProject
18
- include ::Lenna::Cli::Commands::Interface
19
-
20
- # @!attribute [r] app_name
21
- #
22
- private attr_accessor :app_name
23
-
24
- # Initialize the command
25
- #
26
- # @parameter app_name [Array<String>] The name of the app
27
- #
28
- def initialize(app_name)
29
- self.app_name = app_name[0]
30
- end
31
-
32
- # Execute the command
33
- #
34
- # @parameter app_name [String] The name of the app
35
- #
36
- # @return [void]
37
- #
38
- def call
39
- return puts 'Please specify an app name'.red if app_name.nil?
40
-
41
- create_app(app_name) do
42
- create_gemfile
43
- create_config_ru
44
- create_app_directory
45
- end
46
- end
47
-
48
- private
49
-
50
- # Create a directory for the app
51
- #
52
- # @parameter app_name [String] The name of the app
53
- #
54
- # @yield { ... } The block to be executed after the directory
55
- #
56
- # @return [void]
57
- #
58
- def create_app(app_name)
59
- ::Console.info("Creating a new app named #{app_name}")
60
-
61
- ::FileUtils.mkdir_p(app_name)
62
-
63
- ::FileUtils.cd(app_name).tap { yield app_name } if block_given?
64
-
65
- app_name
66
- end
67
-
68
- # Create a new Gemfile for the app. This will be use template
69
- # file in the `templates` directory.
70
- #
71
- # @parameter app_name [String] The name of the app
72
- #
73
- # @return [void]
74
- #
75
- def create_gemfile
76
- { version: Lennarb::VERSION }.then { create_template('gemfile', _1, 'Gemfile') }
77
- end
78
-
79
- # Create a new config.ru for the app. This will be use template
80
- # file in the `templates` directory.
81
- #
82
- # @return [void]
83
- #
84
- def create_config_ru
85
- create_template('config.ru', {})
86
- end
87
-
88
- # Create a new application.rb for the app. This will be use template
89
- # file in the `templates` directory.
90
- #
91
- # @return [void]
92
- #
93
- # @See #create_template
94
- # @See lenna/cli/templates/application
95
- #
96
- def create_app_directory
97
- simple_template = <<~HTML.strip
98
- '<h2>Hello, welcome to Lenna! #{Lennarb::VERSION}</h1>'
99
- HTML
100
- create_template('application', { simple_template: }, 'app/application.rb')
101
- end
102
-
103
- # Method for creating file based on a template
104
- #
105
- # @parameter template_name [String] The name of the template
106
- # @parameter template_data [Hash] The data to be used in the template
107
- #
108
- # @return [void]
109
- #
110
- def create_template(template_name, template_data, file_name = template_name)
111
- Lennarb # rubocop:disable Lint
112
- .root
113
- .join("lib/lenna/cli/templates/#{template_name}.erb")
114
- .then { ::File.read(_1) }
115
- .then { ::ERB.new(_1).result_with_hash(template_data) }
116
- .then do |content|
117
- return ::File.write(file_name, content) unless file_name.include?('/')
118
-
119
- ::FileUtils.mkdir_p(::File.dirname(file_name)) && ::File.write(file_name, content)
120
- end
121
- end
122
- end
123
- end
124
- end
125
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
5
-
6
- module Lenna
7
- module Cli
8
- module Commands
9
- module Interface
10
- def new(args)
11
- raise NotImplementedError
12
- end
13
-
14
- def call
15
- raise NotImplementedError
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,143 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
5
-
6
- require 'console'
7
- require 'optparse'
8
-
9
- module Lenna
10
- module Cli
11
- module Commands
12
- # Command for creating a new app
13
- #
14
- # @private `Since v0.1.0`
15
- #
16
- class StartServer
17
- include Lenna::Cli::Commands::Interface
18
-
19
- # @!attribute [rw] port
20
- # @return [Integer] Port to start the server
21
- #
22
- private attr_accessor :port
23
- # @!attribute [rw] server
24
- # @return [String] Server to start
25
- #
26
- private attr_accessor :server
27
- # @!attribute [rw] command
28
- # @return [String] Command to execute
29
- #
30
- private attr_accessor :command
31
-
32
- def initialize(args)
33
- self.command = args.shift
34
-
35
- options = parse_options!(args)
36
-
37
- self.port = options[:port]
38
- self.server = options[:server]
39
- end
40
-
41
- # Execute the command
42
- #
43
- # @return [void]
44
- #
45
- def call
46
- ::Console.debug("Starting server on port #{port}...")
47
-
48
- case server
49
- in 'puma' | 'falcon' => server then start_server(port:, server:)
50
- else fail ::ArgumentError, ::Console.error("The server '#{server}' is not supported yet.")
51
- end
52
- end
53
-
54
- private
55
-
56
- # Parse the options
57
- #
58
- # @parameter args [Array<String>] The arguments
59
- #
60
- # @return [Hash] The options
61
- #
62
- def parse_options!(args)
63
- options = { port: 4000, server: 'puma' }
64
-
65
- ::OptionParser.new do |opts|
66
- opts.banner = "Usage: lenna #{command} [options]"
67
-
68
- opts.on('-p', '--port [PORT]', Integer, 'Port to start the server') do |port|
69
- options[:port] = port
70
- end
71
-
72
- opts.on('-s', '--server [SERVER]', String, 'Server to start') do |server|
73
- options[:server] = server
74
- end
75
- end.parse!(args)
76
-
77
- options
78
- end
79
-
80
- # Start the server
81
- #
82
- # @paramaeter port [Integer] Port to start the server
83
- # @paramaeter server [String] Server to start
84
- #
85
- # @return [void]
86
- #
87
- def start_server(port:, server:)
88
- return warn_not_installed(server) unless instaled_gem?(server)
89
-
90
- ::File.exist?(config_file) or fail ::StandardError, ::Console.error("'config.ru' not found in #{poroject_name}.")
91
-
92
- system("bundle exec #{server} #{config_file} --port #{port}", chdir: current_path)
93
- rescue ::ArgumentError
94
- ::Console.error("The server '#{server}' is not supported yet.")
95
- rescue ::Interrupt
96
- ::Console.info("\nServer stopped.")
97
- rescue ::StandardError => e
98
- ::Console.error(self, e.message)
99
- end
100
-
101
- # Check if the gem is installed
102
- #
103
- # @parameter name [String] Name of the gem
104
- #
105
- # @return [Boolean]
106
- #
107
- def instaled_gem?(name)
108
- ::Gem::Specification.find_by_name(name)
109
- rescue ::Gem::LoadError
110
- false
111
- end
112
-
113
- # Get the name of the project
114
- #
115
- # @return [String] Name of the project
116
- #
117
- def poroject_name = @current_path.split('/').last
118
-
119
- # Get the path of the project
120
- #
121
- # @return [String] Path of the project
122
- #
123
- def current_path = @current_path ||= ::Dir.pwd
124
-
125
- # Get the path of the config file
126
- #
127
- # @return [String] Path of the config file
128
- #
129
- def config_file = "#{current_path}/config.ru"
130
-
131
- # Warn the user that the gem is not installed
132
- #
133
- # @parameter name [String] Name of the gem
134
- #
135
- # @return [void]
136
- #
137
- def warn_not_installed(name)
138
- ::Console.warn("The gem '#{name}' is not installed. Please install it and try again.")
139
- end
140
- end
141
- end
142
- end
143
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'lennarb'
4
-
5
- class Application
6
- include Lenna::Base
7
-
8
- app.get '/' do |req, res|
9
- res.html(<%= simple_template %>)
10
- end
11
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'app/application'
4
-
5
- run Application.app
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- # [https://rubygems.org/gems/lennarb]
6
- # Lenna is a lightweight and experimental web framework for Ruby. It's designed
7
- # to be modular and easy to use. Also, that's how I affectionately call my wife.
8
- gem 'lennarb', '~> <%= version %>'
9
- # [https://rubygems.org/gems/puma]
10
- # Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server for Ruby/Rack applications.
11
- gem 'puma', '~> 6.4'
12
-
13
- group :development, :test do
14
- end
@@ -1,118 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2023, by Aristóteles Coutinho.
5
-
6
- require 'singleton'
7
-
8
- module Lenna
9
- module Middleware
10
- # The MiddlewareManager class is responsible for managing the middlewares.
11
- #
12
- # @attr global_middlewares [Array] the global middlewares
13
- # @attr middleware_chains_cache [Hash] the middleware chains cache
14
- #
15
- # Middleware chains are cached by action.
16
- # The middlewares that are added to a specific route are added to the
17
- # global middlewares.
18
- #
19
- # @private Since `v0.1.0`
20
- #
21
- class App
22
- include Singleton
23
-
24
- # @return [Array] the global middlewares.
25
- #
26
- attr_accessor :global_middlewares
27
- # @return [Hash] the middleware chains cache.
28
- #
29
- attr_accessor :middleware_chains_cache
30
-
31
- # This method will initialize the global middlewares and the
32
- # middleware chains cache.
33
- #
34
- # @return [void]
35
- #
36
- def initialize
37
- @global_middlewares = []
38
- @middleware_chains_cache = {}
39
- end
40
-
41
- # This method is used to reset the global middlewares and the middleware
42
- # chains cache.
43
- #
44
- # @return [void]
45
- #
46
- def reset!
47
- @global_middlewares = []
48
- @middleware_chains_cache = {}
49
- end
50
-
51
- # This method is used to add a middleware to the global middlewares.
52
- # @parameter middlewares [Array] the middlewares to be used
53
- # @return [void]
54
- #
55
- def use(middlewares)
56
- @global_middlewares += Array(middlewares)
57
- @middleware_chains_cache = {}
58
- end
59
-
60
- # This method is used to fetch or build the middleware chain for the given
61
- # action and route middlewares.
62
- #
63
- # @parameter action [Proc] the action to be executed
64
- # @parameter route_middlewares [Array] the middlewares to be used
65
- # @return [Proc] the middleware chain
66
- #
67
- #
68
- def fetch_or_build_middleware_chain(
69
- action,
70
- route_middlewares,
71
- http_method: nil,
72
- path: nil
73
- )
74
- signature =
75
- if http_method && path
76
- [http_method, path, route_middlewares].hash.to_s
77
- else
78
- ['global', route_middlewares].hash.to_s
79
- end
80
-
81
- @middleware_chains_cache[signature] ||=
82
- build_middleware_chain(action, route_middlewares)
83
- end
84
-
85
- # This method is used to build the middleware chain for the given action
86
- # and middlewares.
87
- #
88
- # @parameter action [Proc] the action to be executed
89
- # @parameter middlewares [Array] the middlewares to be used
90
- # @return [Proc] the middleware chain
91
- #
92
- # ex.
93
- # Given the action:
94
- # `->(req, res) { res << 'Hello' }` and the
95
- # middlewares [mw1, mw2], the middleware
96
- # chain will be:
97
- # mw1 -> mw2 -> action
98
- # The action will be the last middleware in the
99
- # chain.
100
- #
101
- def build_middleware_chain(action, middlewares)
102
- all_middlewares = (@global_middlewares + Array(middlewares))
103
-
104
- all_middlewares.reverse.reduce(action) do |next_middleware, middleware|
105
- ->(req, res) {
106
- middleware.call(
107
- req,
108
- res,
109
- -> {
110
- next_middleware.call(req, res)
111
- }
112
- )
113
- }
114
- end
115
- end
116
- end
117
- end
118
- end