lennarb 0.1.7 → 0.2.0

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