From 01123a6927c3a324f78e1baf46e649f1cc1c094f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Thu, 21 Dec 2023 10:59:09 -0300 Subject: [PATCH 01/16] test: ensure Lennarb.root is set --- test/lib/test_lenna.rb | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 test/lib/test_lenna.rb diff --git a/test/lib/test_lenna.rb b/test/lib/test_lenna.rb new file mode 100644 index 0000000..3c2888f --- /dev/null +++ b/test/lib/test_lenna.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'test_helper' + +class TestLennarb < Minitest::Test + def test_root + assert_equal File.expand_path('../..', __dir__), Lennarb.root.to_s + end +end From 6df717898b7b3036c2566f0b40ff764ae5cb740d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Thu, 21 Dec 2023 11:00:23 -0300 Subject: [PATCH 02/16] feat: add root command Lennar --- lib/lennarb.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/lennarb.rb b/lib/lennarb.rb index 6aa8ce6..90cb5cd 100644 --- a/lib/lennarb.rb +++ b/lib/lennarb.rb @@ -13,3 +13,22 @@ # require 'lenna/application' require 'lennarb/version' + +# Core extensions +# +require 'pathname' + +# Lennarb module +# +module Lennarb + module_function + + # Lennarb root path + # + # @return [Pathname] the root path + # + def root + File.expand_path('..', __dir__) + Pathname.new(File.expand_path('..', __dir__)) + end +end From 0e75f15b4cc0949328e67f4a45d905a88e04484f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Thu, 21 Dec 2023 11:01:13 -0300 Subject: [PATCH 03/16] chore: improve rubocop rules --- .rubocop.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 9c7c712..c9d22bc 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -56,6 +56,15 @@ Metrics/ModuleLength: Metrics/PerceivedComplexity: Max: 10 +Style/MixinGrouping: + Enabled: false + +Style/MethodCalledOnDoEndBlock: + Enabled: false + +Style/SignalException: + Enabled: false + Style/AsciiComments: Enabled: false From 985cef571e98bad9750803385e39d775c77c8e85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Thu, 21 Dec 2023 11:01:54 -0300 Subject: [PATCH 04/16] test: ensure test_create_project generate --- .../lenna/cli/commands/test_create_project.rb | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 test/lib/lenna/cli/commands/test_create_project.rb diff --git a/test/lib/lenna/cli/commands/test_create_project.rb b/test/lib/lenna/cli/commands/test_create_project.rb new file mode 100644 index 0000000..48c839d --- /dev/null +++ b/test/lib/lenna/cli/commands/test_create_project.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2023, by Aristóteles Coutinho. + +require 'fileutils' +require 'lenna/cli/commands/create_project' +require 'test_helper' + +module Lenna + module Cli + module Commands + class TestCreateProject < Minitest::Test + def setup + @app_name = 'test_app' + FileUtils.mkdir_p(@app_name) + + Commands::CreateProject.execute(@app_name) + end + + def test_create_gemfile + @gemfile_content = <<-GEMFILE + # frozen_string_literal: true + + source 'https://rubygems.org' + + # [https://rubygems.org/gems/lennarb] + # Lenna is a lightweight and experimental web framework for Ruby. It's designed + # to be modular and easy to use. Also, that's how I affectionately call my wife. + gem 'lennarb', '~> 0.1.7' + # [https://rubygems.org/gems/falcon] + # A fast, asynchronous, rack-compatible web server. + gem 'falcon', '~> 0.42.3' + + group :development, :test do + end + GEMFILE + + # gemfile_path = File.join(Lennarb::ROOT_PATH, @app_name, 'Gemfile') + gemfile_path = Lennarb.root.join(@app_name, 'Gemfile') + + assert_path_exists gemfile_path + + actual_content = File.read(gemfile_path).gsub(/\s+/, ' ').strip + expected_content = @gemfile_content.gsub(/\s+/, ' ').strip + + assert_equal expected_content, actual_content + end + + def teardown + FileUtils.rm_rf(Lennarb.root.join(@app_name)) + end + end + end + end +end From 664118eece3cdf12962fd5a00f406d53c9b9417e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Thu, 21 Dec 2023 11:02:50 -0300 Subject: [PATCH 05/16] feat: add basic interface to create a new project --- lib/lenna/cli/app.rb | 71 ++++++++++++++++++++++++ lib/lenna/cli/commands/create_project.rb | 64 +++++++++++++++++++++ lib/lenna/cli/commands/interface.rb | 13 +++++ lib/lenna/cli/templates/gemfile.erb | 14 +++++ 4 files changed, 162 insertions(+) create mode 100644 lib/lenna/cli/app.rb create mode 100644 lib/lenna/cli/commands/create_project.rb create mode 100644 lib/lenna/cli/commands/interface.rb create mode 100644 lib/lenna/cli/templates/gemfile.erb diff --git a/lib/lenna/cli/app.rb b/lib/lenna/cli/app.rb new file mode 100644 index 0000000..783f123 --- /dev/null +++ b/lib/lenna/cli/app.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2023, by Aristóteles Coutinho. + +require 'optparse' + +require 'lennarb/version' + +module Lenna + module Cli + # Mediator class for CLI + # + # @private `Since v0.1.0` + # + class App + # @return [Array] Arguments passed to CLI + # + attr_reader :args + + # Initialize a new instance of the class + # + # @parameter args [Array] Arguments passed to CLI + # + def initialize(args) + @args = args + end + + # Execute the command + # + # @return [void] + # + def execute(strategy) + strategy.is_a?(Commands::Interface) or fail ::ArgumentError + + parser!(@args).then { strategy.execute(_1) } + end + + private + + # Parse the options passed to CLI + # + # @parameter args [Array] Arguments passed to CLI + # + # @return [Hash] Options passed to CLI + # + def parser!(args) + options = {} + OptionParser.new do |opts| + opts.banner = 'Usage: lenna [options]' + + opts.on('-v', '--version', 'Print version') do + puts Lenna::VERSION + exit + end + + opts.on('-h', '--help', 'Print help') do + puts opts + exit + end + + opts.on('-n', '--new', 'Create a new app') do + options[:new] = true + end + end.parser!(args) + + options + end + end + end +end diff --git a/lib/lenna/cli/commands/create_project.rb b/lib/lenna/cli/commands/create_project.rb new file mode 100644 index 0000000..8902d56 --- /dev/null +++ b/lib/lenna/cli/commands/create_project.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'colorize' +require 'erb' +require 'fileutils' +require 'lenna/cli/commands/interface' +require 'lennarb/version' + +module Lenna + module Cli + module Commands + # Command for creating a new app + # + # @private `Since v0.1.0` + # + module CreateProject + extend Lenna::Cli::Commands::Interface, self + # Execute the command + # + # @parameter app_name [String] The name of the app + # + # @return [void] + # + def execute(app_name) + return puts 'Please specify an app name' if app_name.nil? + + puts "Creating a new app named #{app_name}".green + create_app(app_name) + create_gemfile(app_name) + end + + private + + # Create a directory for the app + # + # @parameter app_name [String] The name of the app + # + # @return [void] + # + def create_app(app_name) = FileUtils.mkdir_p(app_name) + + # Create a new Gemfile for the app. This will be use template + # file in the `templates` directory. + # + # @parameter app_name [String] The name of the app + # + # @return [void] + # + def create_gemfile(app_name) + FileUtils.cd(app_name).tap do + template_data = { version: Lennarb::VERSION } + + erb_template = + Lennarb.root.join('lib/lenna/cli/templates/gemfile.erb') + .then { File.read(_1) } + .then { ERB.new(_1) } + + File.write('Gemfile', erb_template.result_with_hash(template_data)) + end + end + end + end + end +end diff --git a/lib/lenna/cli/commands/interface.rb b/lib/lenna/cli/commands/interface.rb new file mode 100644 index 0000000..df061f8 --- /dev/null +++ b/lib/lenna/cli/commands/interface.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Lenna + module Cli + module Commands + module Interface + def self.execute(args) + raise NotImplementedError + end + end + end + end +end diff --git a/lib/lenna/cli/templates/gemfile.erb b/lib/lenna/cli/templates/gemfile.erb new file mode 100644 index 0000000..d6a8a4c --- /dev/null +++ b/lib/lenna/cli/templates/gemfile.erb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +# [https://rubygems.org/gems/lennarb] +# Lenna is a lightweight and experimental web framework for Ruby. It's designed +# to be modular and easy to use. Also, that's how I affectionately call my wife. +gem 'lennarb', '~> <%= version %>' +# [https://rubygems.org/gems/falcon] +# A fast, asynchronous, rack-compatible web server. +gem 'falcon', '~> 0.42.3' + +group :development, :test do +end From 8d99937565a2953dae1f4fbb365edd319d32da02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Fri, 22 Dec 2023 23:36:03 -0300 Subject: [PATCH 06/16] chore: update rubocop --- .rubocop.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index c9d22bc..eac8fad 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,8 +8,11 @@ AllCops: Exclude: - "bin/**/*" +Naming/RescuedExceptionsVariableName: + Enabled: false + Layout/LineLength: - Max: 80 + Max: 120 Layout/IndentationStyle: EnforcedStyle: tabs From 66b0572fafa3920920b6db4da2348319045dcf00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Fri, 22 Dec 2023 23:36:32 -0300 Subject: [PATCH 07/16] chpre: add console gem for interactive command line applications --- gems.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gems.rb b/gems.rb index 0e552f3..051b477 100644 --- a/gems.rb +++ b/gems.rb @@ -17,6 +17,9 @@ # [https://rubygems.org/gems/colorize] # Colorize is a Ruby gem used to color text in terminals. gem 'colorize', '~> 1.1' +# [https://rubygems.org/gems/console] +# Console is a Ruby gem used to create interactive command line applications. +gem 'console', '~> 1.23' group :maintenance, optional: true do # [https://rubygems.org/gems/bake-gem] @@ -44,10 +47,10 @@ # [https://rubygems.org/gems/bake] # Bake is a build tool for Ruby projects. It is designed to be simple, # fast and extensible. - gem 'bake' # [https://rubygems.org/gems/puma] + gem 'bake', '~> 0.18.2' # Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for - # Ruby/Rack applications. + # Ruby/Rack applications. gem 'puma', '~> 6.4' # RuboCop is a Ruby code style checking and code formatting tool. It aims to # enforce the community-driven Ruby Style Guide. From 529a178ccd21129190752edfa07e6e94168e8f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Sat, 23 Dec 2023 15:24:47 -0300 Subject: [PATCH 08/16] test: dd test for Middleware::Default::Reload class --- .../lenna/middleware/default/test_reload.rb | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 test/lib/lenna/middleware/default/test_reload.rb diff --git a/test/lib/lenna/middleware/default/test_reload.rb b/test/lib/lenna/middleware/default/test_reload.rb new file mode 100644 index 0000000..5b45955 --- /dev/null +++ b/test/lib/lenna/middleware/default/test_reload.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'test_helper' + +require 'lenna/middleware/default/reload' + +module Middleware + module Default + class TestReload < Minitest::Test + def test_initialize + directories = ['test/lib/lenna/middleware/default/test_reload.rb'] + reload = ::Middleware::Default::Reload.new(directories) + + assert_instance_of(::Middleware::Default::Reload, reload) + end + + def test_call + req = Rack::Request.new({}) + res = Rack::Response.new + next_middleware = proc { res.status = 200 } + reload = Middleware::Default::Reload.new + + reload.call(req, res, next_middleware) + + assert_equal(200, res.status) + end + end + end +end From b513907885d9bb1ff66722bccb7c0bcccbe7ce1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Sat, 23 Dec 2023 15:25:05 -0300 Subject: [PATCH 09/16] feat: add Lenna::Middleware::Default::Reload middleware --- lib/lenna/middleware/default/reload.rb | 97 ++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 lib/lenna/middleware/default/reload.rb diff --git a/lib/lenna/middleware/default/reload.rb b/lib/lenna/middleware/default/reload.rb new file mode 100644 index 0000000..637b9ff --- /dev/null +++ b/lib/lenna/middleware/default/reload.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2023, by Aristóteles Coutinho. + +require 'console' + +# This middleware is used to reload files in development mode. +# +module Lenna + module Middleware + module Default + class Reload + attr_accessor :directories, :files_mtime + + # Initializes a new instance of Middleware::Default::Reload. + # + # @parameter directories [Array] An array of directories to monitor. + # + # @return [Middleware::Default::Reload] A new instance of Middleware::Default::Reload. + def initialize(directories = []) + self.files_mtime = {} + self.directories = directories + + monitor_directories(directories) + end + + # Calls the middleware. + # + # @parameter req [Rack::Request] The request. + # @parameter _res [Rack::Response] The response. + # @parameter next_middleware [Proc] The next middleware. + # + # @return [void] + # + def call(_req, _res, next_middleware) + reload_if_needed + + next_middleware.call + rescue ::StandardError => error + ::Console.error(self, error) + end + + private + + # Reloads files if needed. + # + # @return [void] + # + def reload_if_needed + modified_files = check_for_modified_files + + reload_files(modified_files) unless modified_files.empty? + end + + # Monitors directories for changes. + # + # @parameter directories [Array] An array of directories to monitor. + # + # @return [void] + # + def monitor_directories(directories) + directories.each do |directory| + ::Dir.glob(directory).each { |file| files_mtime[file] = ::File.mtime(file) } + end + end + + # Checks for modified files. + # + # @return [Array] An array of modified files. + # + # @example + # check_for_modified_files #=> ["/path/to/file.rb"] + # + def check_for_modified_files + @files_mtime.select do |file, last_mtime| + ::File.mtime(file) > last_mtime + end.keys + end + + # Reloads files. + # + # @parameter files [Array] An array of files(paths) to reload. + # + # @return [void] + # + def reload_files(files) + files.each do |file| + ::Console.debug("Reloading #{file}") + ::Kernel.load file + @files_mtime[file] = ::File.mtime(file) + end + end + end + end + end +end From c9cf95f28d649bf479bb622dcd132292e7286ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Sat, 23 Dec 2023 15:25:53 -0300 Subject: [PATCH 10/16] refactor: move Lenna::Middleware::TestApp --- test/lib/lenna/middleware/{ => default}/test_app.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/lib/lenna/middleware/{ => default}/test_app.rb (100%) diff --git a/test/lib/lenna/middleware/test_app.rb b/test/lib/lenna/middleware/default/test_app.rb similarity index 100% rename from test/lib/lenna/middleware/test_app.rb rename to test/lib/lenna/middleware/default/test_app.rb From 045c4edfa358f127a77181ed08796c18e8baa968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Sat, 23 Dec 2023 15:26:28 -0300 Subject: [PATCH 11/16] chore: add console dependency and update development dependencies --- lennarb.gemspec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lennarb.gemspec b/lennarb.gemspec index 053baa0..a40c925 100644 --- a/lennarb.gemspec +++ b/lennarb.gemspec @@ -20,14 +20,17 @@ Gem::Specification.new do |spec| 'source_code_uri' => 'https://github.com/aristotelesbr/lennarb' } + spec.executables = ['lenna'] + spec.files = Dir['{lib}/**/*', '*.md', base: __dir__] spec.required_ruby_version = '>= 3.0' spec.add_dependency 'colorize', '~> 1.1' + spec.add_dependency 'console', '~> 1.23' spec.add_dependency 'rack', '~> 3.0', '>= 3.0.8' - spec.add_development_dependency 'bake', '>= 0.18.2' + spec.add_development_dependency 'bake', '~> 0.18', '>= 0.18.2' spec.add_development_dependency 'covered', '~> 0.25.1' spec.add_development_dependency 'puma', '~> 6.4' spec.add_development_dependency 'rack-test', '~> 2.1' From 7c80eadd131df4b00b70669808629a750c8c08b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Sat, 23 Dec 2023 15:26:40 -0300 Subject: [PATCH 12/16] chore: disable RedundantCopDisableDirective and BlockLength metrics in .rubocop.yml --- .rubocop.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index eac8fad..f30fa3a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -34,6 +34,9 @@ Layout/SpaceInLambdaLiteral: Metrics/BlockLength: Enabled: false +Lint/RedundantCopDisableDirective: + Enabled: false + Lint/AmbiguousBlockAssociation: Enabled: false From 84541b8bab1130e0404429c9964f127bcf7eddc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Sat, 23 Dec 2023 15:27:08 -0300 Subject: [PATCH 13/16] docs: add license information to test_lenna.rb --- test/lib/test_lenna.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/lib/test_lenna.rb b/test/lib/test_lenna.rb index 3c2888f..d56e226 100644 --- a/test/lib/test_lenna.rb +++ b/test/lib/test_lenna.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +# Released under the MIT License. +# Copyright, 2023, by Aristóteles Coutinho. + require 'test_helper' class TestLennarb < Minitest::Test From 8e743913e77a12a199b5941066c1da510ba4f380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Sat, 23 Dec 2023 15:27:40 -0300 Subject: [PATCH 14/16] chore: Bump Lennarb version to 0.1.7 --- lib/lennarb/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lennarb/version.rb b/lib/lennarb/version.rb index 6b41f40..66d8e81 100644 --- a/lib/lennarb/version.rb +++ b/lib/lennarb/version.rb @@ -4,7 +4,7 @@ # Copyright, 2023, by Aristóteles Coutinho. module Lennarb - VERSION = '0.1.6' + VERSION = '0.1.7' public_constant :VERSION end From 09948897b255055d754021bc25beca216dea7810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Sat, 23 Dec 2023 15:38:27 -0300 Subject: [PATCH 15/16] chore: Update changelog --- changelog.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/changelog.md b/changelog.md index 8ee9024..b9ae28f 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,41 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.7] - 2023-23-12 + +## Added + +- Add `console` gem to print the logs in the console. +- Add CLI module to run the server. Now, you can run the server with: + +```sh +bundle exec lennarb server +``` + +- Add `--port` option to CLI module. Now, you can run the server in a specific port with: + +```sh +bundle exec lennarb server --port 3000 +``` + +- Add `Reload` middleware to reload the application in development environment. You can import and use this middleware in your application. Ex. + +```rb +# app.rb + +require 'lenna/middleware/default/reload' + +app = Lenna::Application.new + +app.use Lenna::Middleware::Default::Reload +``` + +In the next version, this middleware will be available by default in development environment. + +## Remove + +- Remove `Logging` and `ErrorHandling` middlewares from any environment. Now, theses middlewares are only available in development environment. + ## [0.1.6] - 2023-21-12 ### Changed From fded78bfd200581a5893bafa8581a2e2f6cf2be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arist=C3=B3teles=20Coutinho?= Date: Sat, 23 Dec 2023 15:49:36 -0300 Subject: [PATCH 16/16] chore: update changelog.md --- changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/changelog.md b/changelog.md index b9ae28f..438d725 100644 --- a/changelog.md +++ b/changelog.md @@ -36,6 +36,15 @@ app.use Lenna::Middleware::Default::Reload In the next version, this middleware will be available by default in development environment. +- Add `root` method to `Lennarb` module to get the root path of the project. Ex. + +```rb +# app.rb + +Lennarb.root.join('app.rb') +# => /home/user/project/app.rb +``` + ## Remove - Remove `Logging` and `ErrorHandling` middlewares from any environment. Now, theses middlewares are only available in development environment.