From f122881a7babe373ce2715d45a8df2cb406bdeb5 Mon Sep 17 00:00:00 2001
From: Augustin Gottlieb Pequeno <33221555+aguspe@users.noreply.github.com>
Date: Mon, 29 Jul 2024 15:30:13 +0200
Subject: [PATCH] Start adding dynamic plugin support (#111)
* Start adding dynamic plugin support
* Correct extra space
* Update Gemfile
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Add and remove commands dynamically
* Plugin working
* Plugin command running
* Fix tests
* Fix plugins and update read_me
* Update version
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
lib/commands/loaded_commands.rb | 9 ++
lib/commands/plugin_commands.rb | 34 ++++++++
lib/generators/menu_generator.rb | 6 +-
lib/generators/templates/common/read_me.tt | 19 ++---
lib/plugin/plugin.rb | 99 ++++++++++++++++++++++
lib/plugin/plugin_exposer.rb | 77 +++++++++++++++++
lib/ruby_raider.rb | 27 +++++-
lib/version | 2 +-
plugins.yml | 2 +
9 files changed, 258 insertions(+), 17 deletions(-)
create mode 100644 lib/commands/loaded_commands.rb
create mode 100644 lib/commands/plugin_commands.rb
create mode 100644 lib/plugin/plugin.rb
create mode 100644 lib/plugin/plugin_exposer.rb
create mode 100644 plugins.yml
diff --git a/lib/commands/loaded_commands.rb b/lib/commands/loaded_commands.rb
new file mode 100644
index 0000000..e5f8cba
--- /dev/null
+++ b/lib/commands/loaded_commands.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'thor'
+require_relative '../plugin/plugin'
+
+module RubyRaider
+ class LoadedCommands < Thor
+ end
+end
diff --git a/lib/commands/plugin_commands.rb b/lib/commands/plugin_commands.rb
new file mode 100644
index 0000000..f0c1726
--- /dev/null
+++ b/lib/commands/plugin_commands.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'thor'
+require_relative '../plugin/plugin'
+
+module RubyRaider
+ # :reek:FeatureEnvy { enabled: false }
+ # :reek:UtilityFunction { enabled: false }
+ class PluginCommands < Thor
+ desc 'add [NAME]', 'Adds a plugin to your project'
+
+ def add(plugin_name)
+ Plugin.add_plugin(plugin_name)
+ end
+
+ desc 'delete [NAME]', 'Deletes a plugin from your project'
+
+ def delete(plugin_name)
+ Plugin.delete_plugin(plugin_name)
+ end
+
+ desc 'local', 'Lists all the plugin in your project'
+
+ def local
+ pp Plugin.installed_plugins
+ end
+
+ desc 'list', 'Lists all the available plugin'
+
+ def list
+ pp Plugin.available_plugins
+ end
+ end
+end
diff --git a/lib/generators/menu_generator.rb b/lib/generators/menu_generator.rb
index 984fadd..d284746 100644
--- a/lib/generators/menu_generator.rb
+++ b/lib/generators/menu_generator.rb
@@ -84,11 +84,15 @@ def yes_no_menu_choices
end
def select_automation_framework(menu)
+ automation_options(menu)
+ menu.choice :Quit, -> { exit }
+ end
+
+ def automation_options(menu)
menu.choice :Selenium, -> { choose_test_framework('selenium') }
menu.choice :Appium, -> { choose_test_framework('appium') }
menu.choice :Watir, -> { choose_test_framework('watir') }
menu.choice :Applitools, -> { choose_test_framework('applitools') }
menu.choice :Axe, -> { choose_test_framework('axe') }
- menu.choice :Quit, -> { exit }
end
end
diff --git a/lib/generators/templates/common/read_me.tt b/lib/generators/templates/common/read_me.tt
index 9693067..a192429 100644
--- a/lib/generators/templates/common/read_me.tt
+++ b/lib/generators/templates/common/read_me.tt
@@ -4,12 +4,9 @@
- This is a gem to make setup and start of UI automation projects easier.
-
Explore the docs ยป
@@ -79,13 +76,13 @@ Select the ones you will like to work with.
If you already know which frameworks you want to use, you can do:
```ruby
-raider new [name_of_project] - p framework : [framework] automation : [automation_type] visual : [boolean] axe : [boolean]
+raider new [name_of_project] p framework : [framework] automation : [automation_type]
```
An example of the command above would be:
```ruby
-raider new test_project -p framework : rspec automation: selenium visual : false axe : true
+raider new test_project p framework : rspec automation: selenium
```
Where [frameworks] is a comma separated list of the frameworks you want to use.
@@ -120,15 +117,15 @@ Ruby Raider also supports scaffolding:
* To create a new steps definition you do: ```raider g steps [STEPS_NAME]```
* To create both a page/spec or a page/feature/steps you do: ```raider g scaffold [SCAFFOLD_NAME]```
-It's possible to add the option --path or -p if you want to specify where to create your features, pages, helpers and
+It's possible to add the option --path or p if you want to specify where to create your features, pages, helpers and
specs.
If you want to set the default path for the creation of your features, helpers and specs:
```ruby
-raider u path [PATH_NAME] - -feature or -f
-raider u path [PATH_NAME] - -spec or -s
-raider u path [PATH_NAME] - -helper or -h
+raider u path [PATH_NAME] - -feature or f
+raider u path [PATH_NAME] - -spec or s
+raider u path [PATH_NAME] - -helper or h
```
If you don't specify an option, path will assume you want to change the default path for pages.
@@ -139,4 +136,4 @@ To initialise Appium server run this command:
```ruby
raider u start_appium
-```
\ No newline at end of file
+```
diff --git a/lib/plugin/plugin.rb b/lib/plugin/plugin.rb
new file mode 100644
index 0000000..da43e08
--- /dev/null
+++ b/lib/plugin/plugin.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+require 'yaml'
+require_relative 'plugin_exposer'
+
+module RubyRaider
+ module Plugin
+ class << self
+ def add_plugin(plugin_name)
+ return pp 'The plugin was not found' unless available?(plugin_name)
+ return pp 'The plugin is already installed' if installed?(plugin_name)
+
+ pp "Adding #{plugin_name}..."
+ add_plugin_to_gemfile(plugin_name)
+ system('bundle install')
+ PluginExposer.expose_commands(plugin_name)
+ pp "The plugin #{plugin_name} is added"
+ end
+
+ def delete_plugin(plugin_name)
+ return 'The plugin is not installed' unless installed_plugins.include?(plugin_name)
+
+ pp "Deleting #{plugin_name}..."
+ remove_plugin_from_gemfile(plugin_name)
+ PluginExposer.remove_command(plugin_name)
+ system('bundle install')
+ pp "The plugin #{plugin_name} is deleted"
+ end
+
+ def installed_plugins
+ parsed_gemfile = File.readlines('Gemfile').map { |line| line.sub('gem ', '').strip.delete("'") }
+ parsed_gemfile.select { |line| available_plugins.include?(line) }
+ end
+
+ def available_plugins
+ plugins['plugins']
+ end
+
+ def installed?(plugin_name)
+ installed_plugins.include?(plugin_name)
+ end
+
+ def available?(plugin_name)
+ available_plugins.include?(plugin_name)
+ end
+
+ def camelize(str)
+ str.split('_').collect(&:capitalize).join
+ end
+
+ private
+
+ def add_plugin_to_gemfile(plugin_name)
+ File.open('Gemfile', 'a') do |file|
+ file.puts "\n# Ruby Raider Plugins\n" unless comment_present?
+ file.puts "gem '#{plugin_name}'" unless plugin_present?(plugin_name)
+ end
+ end
+
+ def remove_plugin_from_gemfile(plugin_name)
+ output_lines = remove_plugins_and_comments(plugin_name)
+ update_gemfile(output_lines)
+ end
+
+ def last_plugin?
+ installed_plugins.count == 1
+ end
+
+ def plugins
+ @plugins ||= YAML.load_file(File.expand_path('plugins.yml'))
+ end
+
+ def read_gemfile
+ File.readlines('Gemfile')
+ end
+
+ def comment_present?
+ read_gemfile.grep(/Ruby Raider Plugins/).any?
+ end
+
+ def plugin_present?(plugin_name)
+ read_gemfile.grep(/#{plugin_name}/).any?
+ end
+
+ def remove_plugins_and_comments(plugin_name)
+ read_gemfile.reject do |line|
+ line.include?(plugin_name) || line.include?('Ruby Raider Plugins') && last_plugin?
+ end
+ end
+
+ # :reek:NestedIterators { enabled: false }
+ def update_gemfile(output_lines)
+ File.open('Gemfile', 'w') do |file|
+ output_lines.each { |line| file.puts line }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/plugin/plugin_exposer.rb b/lib/plugin/plugin_exposer.rb
new file mode 100644
index 0000000..4770a83
--- /dev/null
+++ b/lib/plugin/plugin_exposer.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require_relative '../plugin/plugin'
+
+module RubyRaider
+ module PluginExposer
+ class << self
+ FILE_PATH = File.expand_path('../commands/loaded_commands.rb', __dir__)
+ # :reek:NestedIterators { enabled: false }
+ def expose_commands(plugin_name)
+ return pp 'The plugin is already installed' if plugin_present?(plugin_name)
+
+ commands = read_loaded_commands
+
+ File.open(FILE_PATH, 'w') do |file|
+ commands.each do |line|
+ file.puts line
+ file.puts require_plugin(plugin_name, line)
+ file.puts select_command_formatting(plugin_name, line)
+ end
+ end
+ end
+
+ def remove_command(plugin_name)
+ return pp 'The plugin is not installed' unless plugin_present?(plugin_name)
+
+ delete_plugin_command(plugin_name)
+ end
+
+ private
+
+ def any_commands?
+ read_loaded_commands.any? { |line| line.include?('subcommand') }
+ end
+
+ def read_loaded_commands
+ File.readlines(FILE_PATH)
+ end
+
+ def formatted_command_without_space(plugin_name)
+ "desc '#{plugin_name}', 'Provides access to all the commands for #{plugin_name}'\n" \
+ "subcommand '#{plugin_name}', #{Plugin.camelize(plugin_name)}::PluginCommands"
+ end
+
+ def formatted_command_with_space(plugin_name)
+ "\n#{formatted_command_without_space(plugin_name)}"
+ end
+
+ def plugin_present?(plugin_name)
+ read_loaded_commands.grep(/#{plugin_name}/).any?
+ end
+
+ def select_command_formatting(plugin_name, line)
+ if line.strip == 'class LoadedCommands < Thor' && !any_commands?
+ formatted_command_without_space(plugin_name)
+ elsif any_commands?
+ formatted_command_with_space(plugin_name)
+ end
+ end
+
+ def require_plugin(plugin_name, line)
+ "require '#{plugin_name}'" if line.include?("require 'thor'") && !plugin_present?(plugin_name)
+ end
+
+ # :reek:NestedIterators { enabled: false }
+ def delete_plugin_command(plugin_name)
+ output_lines = read_loaded_commands.reject do |line|
+ line.include?(plugin_name) if plugin_present?(plugin_name)
+ end
+
+ File.open(FILE_PATH, 'w') do |file|
+ output_lines.each { |line| file.puts line }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/ruby_raider.rb b/lib/ruby_raider.rb
index 1e45ccd..e97034f 100644
--- a/lib/ruby_raider.rb
+++ b/lib/ruby_raider.rb
@@ -1,5 +1,8 @@
# frozen_string_literal: true
+require_relative '../lib/plugin/plugin'
+require_relative '../lib/commands/plugin_commands'
+require_relative '../lib/commands/loaded_commands'
require_relative '../lib/commands/scaffolding_commands'
require_relative '../lib/commands/utility_commands'
@@ -7,9 +10,19 @@
# :reek:UtilityFunction { enabled: false }
module RubyRaider
class Raider < Thor
+ no_tasks do
+ def self.plugin_commands?
+ File.readlines(File.expand_path('commands/loaded_commands.rb', __dir__)).any? do |line|
+ line.include?('subcommand')
+ end
+ end
+
+ def current_version = File.read(File.expand_path('version', __dir__)).strip
+ end
+
desc 'new [PROJECT_NAME]', 'Creates a new framework based on settings picked'
option :parameters,
- type: :hash, required: false, desc: 'Parameters to avoid using the menu', aliases: '-p'
+ type: :hash, required: false, desc: 'Parameters to avoid using the menu', aliases: 'p'
def new(project_name)
params = options[:parameters]
@@ -22,7 +35,7 @@ def new(project_name)
MenuGenerator.new(project_name).generate_choice_menu
end
- map '-n' => 'new'
+ map 'n' => 'new'
desc 'version', 'It shows the version of Ruby Raider you are currently using'
@@ -40,8 +53,14 @@ def version
subcommand 'utility', UtilityCommands
map 'u' => 'utility'
- no_commands do
- def current_version = File.read(File.expand_path('version', __dir__)).strip
+ desc 'plugin_manager', 'Provides access to all the commands to manager your plugins'
+ subcommand 'plugin_manager', PluginCommands
+ map 'pm' => 'plugin_manager'
+
+ if plugin_commands?
+ desc 'plugins', 'loaded plugin commands'
+ subcommand 'plugins', LoadedCommands
+ map 'ps' => 'plugins'
end
end
end
diff --git a/lib/version b/lib/version
index 0383441..b5d0ec5 100644
--- a/lib/version
+++ b/lib/version
@@ -1 +1 @@
-0.9.5
\ No newline at end of file
+0.9.8
\ No newline at end of file
diff --git a/plugins.yml b/plugins.yml
new file mode 100644
index 0000000..95bf38b
--- /dev/null
+++ b/plugins.yml
@@ -0,0 +1,2 @@
+plugins:
+ - great_axe
\ No newline at end of file