diff --git a/lib/dry_module_generator.rb b/lib/dry_module_generator.rb
index fbec52a..52cda55 100644
--- a/lib/dry_module_generator.rb
+++ b/lib/dry_module_generator.rb
@@ -4,9 +4,10 @@
require "rails/generators"
require_relative "dry_module_generator/install/installer"
require_relative "dry_module_generator/module/generator"
-require_relative "dry_module_generator/uninstall/uninstaller"
+require_relative "dry_module_generator/config/configuration"
+require_relative "dry_module_generator/config/generator_configuration"
module DryModuleGenerator
class Error < StandardError; end
- # Your code goes here...
+ class ConfigurationError < StandardError; end
end
diff --git a/lib/dry_module_generator/config/configuration.rb b/lib/dry_module_generator/config/configuration.rb
new file mode 100644
index 0000000..5cbbd83
--- /dev/null
+++ b/lib/dry_module_generator/config/configuration.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module DryModuleGenerator
+ module Config
+ module Configuration
+ def configure
+ yield self
+ end
+
+ def define_setting(name, default = nil)
+ class_variable_set("@@#{name}", default)
+
+ define_class_method "#{name}=" do |value|
+ class_variable_set("@@#{name}", value)
+ end
+
+ define_class_method name do
+ class_variable_get("@@#{name}")
+ end
+ end
+
+ private
+
+ def define_class_method(name, &block)
+ (class << self; self; end).instance_eval do
+ define_method name, &block
+ end
+ end
+ end
+ end
+end
diff --git a/lib/dry_module_generator/config/generator_configuration.rb b/lib/dry_module_generator/config/generator_configuration.rb
new file mode 100644
index 0000000..243571e
--- /dev/null
+++ b/lib/dry_module_generator/config/generator_configuration.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module DryModuleGenerator
+ module Config
+ module GeneratorConfiguration
+ extend Configuration
+
+ define_setting :include_views, true
+ define_setting :css_framework, "bootstrap5"
+ define_setting :html_style, "erb"
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/install/installer.rb b/lib/dry_module_generator/install/installer.rb
index 3009500..cfa3cac 100644
--- a/lib/dry_module_generator/install/installer.rb
+++ b/lib/dry_module_generator/install/installer.rb
@@ -3,71 +3,12 @@
module DryModuleGenerator
class Installer < Rails::Generators::Base
source_root File.expand_path("templates", __dir__)
- namespace "dry_module:setup"
+ namespace "dry_module:install"
- def update_application_record
- file_path = "app/models/application_record.rb"
- class_name = "ApplicationRecord"
-
- # Read the existing content of the file
- file_content = File.read(file_path)
-
- # Define the code you want to add
- additional_code = "
-
- def self.save(record)
- record.tap(&:save)
- end
-
- def self.save!(record)
- record.tap(&:save!)
- end
-
- def self.delete!(record)
- record.tap(&:destroy!)
- end"
-
- # Check if the class is present in the file
- if file_content.include?("class #{class_name}") && !file_content.include?("def self.save!")
- # If the class is present, find the end of the class definition and add the code there
- class_definition_end = file_content.index("end", file_content.index("class #{class_name}"))
- if class_definition_end
- inject_code_position = class_definition_end - 1
- file_content.insert(inject_code_position, additional_code)
- File.write(file_path, file_content)
- else
- puts "Error: Unable to find the end of the class definition in #{file_path}."
- end
- else
- puts "Error: Unable to find the class definition for #{class_name} in #{file_path}."
- end
- end
-
- def create_utils
- template("utils/contract_validator.rb", File.join("lib/utils/contract_validator.rb"))
- template("utils/types.rb", File.join("lib/utils/types.rb"))
- template("utils/application_contract.rb", File.join("lib/utils/application_contract.rb"))
- template("utils/application_struct.rb", File.join("lib/utils/application_struct.rb"))
- template("utils/application_read_struct.rb", File.join("lib/utils/application_read_struct.rb"))
- template(
- "utils/injection/controller_resolve_strategy.rb",
- File.join("lib/utils/injection/controller_resolve_strategy.rb")
- )
- end
-
- def create_application_service
- template("services/application_service.rb", File.join("app/services/application_service.rb"))
- end
-
- def create_constraint_error
- template("errors/constraint_error.rb", File.join("app/errors/constraint_error.rb"))
- end
-
- def create_initializers
- template("initializers/container.rb", File.join("config/initializers/container.rb"))
- template("initializers/dependency_injection.rb", File.join("config/initializers/dependency_injection.rb"))
- template("initializers/dry_struct_generator.rb", File.join("config/initializers/dry_struct_generator.rb"))
- template("initializers/routes.rb", File.join("config/initializers/routes.rb"))
+ def create_utility_files
+ full_file_paths = Dir.glob(File.join(self.class.source_root, '**', '**')).filter {|it| it.include?('.rb.tt') }
+ file_names = full_file_paths.map {|it| it.split('templates/').last.chop.chop.chop }
+ file_names.each {|name| template(name, File.join(name)) }
end
def update_application
@@ -87,18 +28,7 @@ def update_application_controller
return if file_content.include?("ConstraintError")
inject_into_class file_path, "ApplicationController" do
- " include Import.inject[validator: 'contract_validator']
-
- rescue_from(ConstraintError) do |e|
- @form = e.validator
- if action_name == 'create'
- render :new, status: :unprocessable_entity
- elsif action_name == 'update'
- render :edit, status: :unprocessable_entity
- end
- end
-
-"
+ " include Import.inject[validator: 'contract_validator']"
end
end
@@ -106,10 +36,9 @@ def update_application_helper
file_path = "app/helpers/application_helper.rb"
file_content = File.read(file_path)
- return if file_content.include?("def show_error")
-
- inject_into_module file_path, "ApplicationHelper" do
- <<-"CODE"
+ unless file_content.include?("def show_error")
+ inject_into_module file_path, "ApplicationHelper" do
+ <<-"CODE"
def show_error(validator, keys)
return unless validator.errors
keys = [keys] unless keys.is_a?(Array)
@@ -134,12 +63,75 @@ def find_value(hash, keys)
end
result
end
- CODE
+
+ def invalid?(validator, keys)
+ if show_error(validator, keys)
+ 'invalid'
+ else
+ ''
+ end
+ end
+ CODE
+ end
+ end
+
+ unless file_content.include?("include Pagy::Frontend")
+ inject_into_module file_path, "ApplicationHelper" do
+ " include Pagy::Frontend
+
+"
+ end
end
end
- def create_javascripts
- template("javascript/controllers/form_controller.js", File.join("app/javascript/controllers/form_controller.js"))
+ def update_gemfile
+ file_path = "Gemfile"
+ file_content = File.read(file_path)
+
+ append_to_file(file_path) do
+ "
+gem 'sass-rails'"
+ end unless file_content.include?('sass-rails')
+
+ append_to_file(file_path) do
+ "
+gem 'dry-validation'"
+ end unless file_content.include?('dry-validation')
+
+ append_to_file(file_path) do
+ "
+gem 'dry-struct'"
+ end unless file_content.include?('dry-struct')
+
+ append_to_file(file_path) do
+ "
+gem 'dry-system', '~> 1'"
+ end unless file_content.include?('dry-system')
+
+ append_to_file(file_path) do
+ "
+gem 'dry_struct_generator'"
+ end unless file_content.include?('dry_struct_generator')
+
+ append_to_file(file_path) do
+ "
+gem 'dry_object_mapper'"
+ end unless file_content.include?('dry_object_mapper')
+
+ append_to_file(file_path) do
+ "
+gem 'pagy'"
+ end unless file_content.include?('pagy')
+
+ append_to_file(file_path) do
+ "
+gem 'ransack'"
+ end unless file_content.include?('ransack')
+
+ append_to_file(file_path) do
+ "
+gem 'rails_event_store'"
+ end unless file_content.include?('rails_event_store')
end
end
end
diff --git a/lib/dry_module_generator/install/templates/errors/constraint_error.rb.tt b/lib/dry_module_generator/install/templates/app/errors/constraint_error.rb.tt
similarity index 100%
rename from lib/dry_module_generator/install/templates/errors/constraint_error.rb.tt
rename to lib/dry_module_generator/install/templates/app/errors/constraint_error.rb.tt
diff --git a/lib/dry_module_generator/install/templates/app/models/aggregate_root.rb.tt b/lib/dry_module_generator/install/templates/app/models/aggregate_root.rb.tt
new file mode 100644
index 0000000..3429c8d
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/app/models/aggregate_root.rb.tt
@@ -0,0 +1,14 @@
+class AggregateRoot < ApplicationRecord
+ self.abstract_class = true
+
+ def domain_events
+ @domain_events ||= []
+ end
+
+ def apply_event(event)
+ domain_events << event
+ self.updated_at = DateTime.now
+ end
+
+ attr_writer :domain_events
+end
diff --git a/lib/dry_module_generator/install/templates/app/models/application_record.rb.tt b/lib/dry_module_generator/install/templates/app/models/application_record.rb.tt
new file mode 100644
index 0000000..c4e22d2
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/app/models/application_record.rb.tt
@@ -0,0 +1,11 @@
+class ApplicationRecord < ActiveRecord::Base
+ primary_abstract_class
+
+ def self.save!(record)
+ record.tap(&:save!)
+ end
+
+ def self.delete!(record)
+ record.tap(&:destroy!)
+ end
+end
diff --git a/lib/dry_module_generator/install/templates/app/models/current_user_repository.rb.tt b/lib/dry_module_generator/install/templates/app/models/current_user_repository.rb.tt
new file mode 100644
index 0000000..9987653
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/app/models/current_user_repository.rb.tt
@@ -0,0 +1,3 @@
+class CurrentUserRepository < ActiveSupport::CurrentAttributes
+ attribute :authenticated_identity
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/install/templates/app/services/application_service.rb.tt b/lib/dry_module_generator/install/templates/app/services/application_service.rb.tt
new file mode 100644
index 0000000..cbab8a9
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/app/services/application_service.rb.tt
@@ -0,0 +1,20 @@
+class ApplicationService
+ include Import[event_publisher: 'events.publisher', current_user_repository: 'current_user_repository']
+ include Pagy::Backend
+
+ def paginate_collection(collection:, mapper:, page:, page_size:, filter:, size: 5, options: {})
+ result = pagy(collection.ransack(filter).result, items: page_size, page: page, size: size)
+
+ pagy_metadata = result[0]
+ paginated_data = result[1]
+
+ PaginationListDto.new(
+ data: map_into(paginated_data, mapper, options),
+ pagination: map_into(pagy_metadata, PaginationDto)
+ )
+ end
+
+ def map_into(data, mapper, options = {})
+ DryObjectMapper::Mapper.call(data, mapper, options)
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/install/templates/app/services/async_event_handler.rb.tt b/lib/dry_module_generator/install/templates/app/services/async_event_handler.rb.tt
new file mode 100644
index 0000000..957e6e1
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/app/services/async_event_handler.rb.tt
@@ -0,0 +1,7 @@
+class AsyncEventHandler < ApplicationJob
+ def call(_event); end
+
+ def perform(serialized_event)
+ call(App::Container.resolve('events.publisher').deserialize(serialized_event, YAML))
+ end
+end
diff --git a/lib/dry_module_generator/install/templates/app/services/async_thread_event_handler.rb.tt b/lib/dry_module_generator/install/templates/app/services/async_thread_event_handler.rb.tt
new file mode 100644
index 0000000..fea0ebf
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/app/services/async_thread_event_handler.rb.tt
@@ -0,0 +1,3 @@
+# Just a marker abstract class
+class AsyncThreadEventHandler
+end
diff --git a/lib/dry_module_generator/install/templates/app/views/shared/_flash.html.erb.tt b/lib/dry_module_generator/install/templates/app/views/shared/_flash.html.erb.tt
new file mode 100644
index 0000000..3dc88c7
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/app/views/shared/_flash.html.erb.tt
@@ -0,0 +1,12 @@
+<%%
+ key = flash&.keys&.first&.to_s || ""
+ value = flash[key] || []
+ value = [value] unless value.is_a?(Array)
+%>
+
+
+
\ No newline at end of file
diff --git a/lib/dry_module_generator/install/templates/config/initializers/active_job_serializers.rb.tt b/lib/dry_module_generator/install/templates/config/initializers/active_job_serializers.rb.tt
new file mode 100644
index 0000000..0447daf
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/config/initializers/active_job_serializers.rb.tt
@@ -0,0 +1,3 @@
+require 'utils/command_serializer'
+
+Rails.application.config.active_job.custom_serializers << CommandSerializer
diff --git a/lib/dry_module_generator/install/templates/config/initializers/container.rb.tt b/lib/dry_module_generator/install/templates/config/initializers/container.rb.tt
new file mode 100644
index 0000000..87c43d4
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/config/initializers/container.rb.tt
@@ -0,0 +1,55 @@
+Dir[File.join(Rails.root, 'lib', 'utils', 'events', '*.rb')].each do |file|
+ require File.join(File.dirname(file), File.basename(file, File.extname(file)))
+end
+
+Dir[File.join(Rails.root, 'lib', 'utils', 'concurrent', '*.rb')].each do |file|
+ require File.join(File.dirname(file), File.basename(file, File.extname(file)))
+end
+
+module App
+ class Container < Dry::System::Container
+ register('contract_validator') { ContractValidator.new }
+ register('current_user_repository') { CurrentUserRepository }
+ if Rails.env.test?
+ register('events.client', RailsEventStore::Client.new(
+ repository: NoOpEventRepository.new,
+ mapper: ToYAMLEventMapper.new,
+ dispatcher: RubyEventStore::ComposedDispatcher.new(
+ RailsEventStore::ImmediateAsyncDispatcher.new(scheduler: ImmediateActiveJobEventScheduler.new(serializer: YAML)),
+ RailsEventStore::ImmediateAsyncDispatcher.new(scheduler: ImmediateEventScheduler.new(event_deserializer: YAML))
+ )
+ ))
+ else
+ register('events.client', RailsEventStore::Client.new(
+ repository: NoOpEventRepository.new,
+ mapper: ToYAMLEventMapper.new,
+ dispatcher: RubyEventStore::ComposedDispatcher.new(
+ RailsEventStore::ImmediateAsyncDispatcher.new(scheduler: ActiveJobEventScheduler.new(serializer: YAML)),
+ RailsEventStore::ImmediateAsyncDispatcher.new(
+ scheduler: AsyncThreadEventScheduler.new(
+ job_scheduler: AsyncJobScheduler.new(
+ executor_service: Concurrent::ThreadPoolExecutor.new(
+ min_threads: 1,
+ max_threads: (ENV['BACKGROUND_THREADS'] || 5).to_i,
+ max_queue: ENV['BACKGROUND_TASK_POOL_SIZE'].to_i,
+ fallback_policy: :caller_runs
+ )
+ ),
+ event_deserializer: YAML
+ )
+ )
+ )
+ ))
+ end
+
+ register('events.publisher', memoize: true) { MetadataEventPublisher.new }
+ end
+end
+
+Import = App::Container.injector
+
+Rails.configuration.after_initialize do
+ Dir[File.join(Rails.root, '*', 'lib', '*', 'infra', 'system', 'provider_source.rb')].each do |file|
+ require File.join(File.dirname(file), File.basename(file, File.extname(file)))
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/install/templates/initializers/dependency_injection.rb.tt b/lib/dry_module_generator/install/templates/config/initializers/dependency_injection.rb.tt
similarity index 52%
rename from lib/dry_module_generator/install/templates/initializers/dependency_injection.rb.tt
rename to lib/dry_module_generator/install/templates/config/initializers/dependency_injection.rb.tt
index a3feadd..2e6bcbf 100644
--- a/lib/dry_module_generator/install/templates/initializers/dependency_injection.rb.tt
+++ b/lib/dry_module_generator/install/templates/config/initializers/dependency_injection.rb.tt
@@ -1,3 +1,5 @@
+require 'utils/injection/active_job_strategy'
require 'utils/injection/controller_resolve_strategy'
+Dry::AutoInject::Strategies.register('active_job', Dry::AutoInject::Strategies::ActiveJobStrategy)
Dry::AutoInject::Strategies.register('inject', Dry::AutoInject::Strategies::ControllerResolveStrategy)
diff --git a/lib/dry_module_generator/install/templates/config/initializers/dry_module_generator.rb.tt b/lib/dry_module_generator/install/templates/config/initializers/dry_module_generator.rb.tt
new file mode 100644
index 0000000..4d9a471
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/config/initializers/dry_module_generator.rb.tt
@@ -0,0 +1,5 @@
+DryModuleGenerator::Config::GeneratorConfiguration.configure do |config|
+ config.include_views = true
+ config.css_framework = "bootstrap5"
+ config.html_style = "erb"
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/install/templates/initializers/dry_struct_generator.rb.tt b/lib/dry_module_generator/install/templates/config/initializers/dry_struct_generator.rb.tt
similarity index 100%
rename from lib/dry_module_generator/install/templates/initializers/dry_struct_generator.rb.tt
rename to lib/dry_module_generator/install/templates/config/initializers/dry_struct_generator.rb.tt
diff --git a/lib/dry_module_generator/install/templates/initializers/routes.rb.tt b/lib/dry_module_generator/install/templates/config/initializers/routes.rb.tt
similarity index 100%
rename from lib/dry_module_generator/install/templates/initializers/routes.rb.tt
rename to lib/dry_module_generator/install/templates/config/initializers/routes.rb.tt
diff --git a/lib/dry_module_generator/install/templates/initializers/container.rb.tt b/lib/dry_module_generator/install/templates/initializers/container.rb.tt
deleted file mode 100644
index e64df98..0000000
--- a/lib/dry_module_generator/install/templates/initializers/container.rb.tt
+++ /dev/null
@@ -1,13 +0,0 @@
-module App
- class Container < Dry::System::Container
- register('contract_validator') { ContractValidator.new }
- end
-end
-
-Import = App::Container.injector
-
-Rails.configuration.after_initialize do
- Dir[File.join(Rails.root, '*', 'lib', '*', 'infra', 'system', 'provider_source.rb')].each do |file|
- require File.join(File.dirname(file), File.basename(file, File.extname(file)))
- end
-end
\ No newline at end of file
diff --git a/lib/dry_module_generator/install/templates/javascript/controllers/form_controller.js.tt b/lib/dry_module_generator/install/templates/javascript/controllers/form_controller.js.tt
deleted file mode 100644
index 0576046..0000000
--- a/lib/dry_module_generator/install/templates/javascript/controllers/form_controller.js.tt
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Controller } from "@hotwired/stimulus"
-
-export default class extends Controller {
- connect() {
- const form = this.element;
- form.addEventListener('submit', event => {
- if (!form.checkValidity()) {
- event.preventDefault()
- event.stopPropagation()
- }
- form.classList.add('was-validated');
- }, false)
- }
-}
diff --git a/lib/dry_module_generator/install/templates/lib/utils/application_contract.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/application_contract.rb.tt
new file mode 100644
index 0000000..4e43dec
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/application_contract.rb.tt
@@ -0,0 +1,19 @@
+class ApplicationContract < Dry::Validation::Contract
+ attr_accessor :params, :errors
+
+ def initialize(errors: {}, params: {})
+ super()
+ self.errors = errors
+ self.params = params
+ end
+
+ def self.command
+ DryStructGenerator::StructGenerator.new.call(self)
+ end
+
+ def to_h
+ self.class.schema.rules.keys.inject({}) do |hash, key|
+ hash.merge({ key => self.send(key) })
+ end
+ end
+end
diff --git a/lib/dry_module_generator/install/templates/utils/application_read_struct.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/application_read_struct.rb.tt
similarity index 100%
rename from lib/dry_module_generator/install/templates/utils/application_read_struct.rb.tt
rename to lib/dry_module_generator/install/templates/lib/utils/application_read_struct.rb.tt
diff --git a/lib/dry_module_generator/install/templates/utils/application_struct.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/application_struct.rb.tt
similarity index 100%
rename from lib/dry_module_generator/install/templates/utils/application_struct.rb.tt
rename to lib/dry_module_generator/install/templates/lib/utils/application_struct.rb.tt
diff --git a/lib/dry_module_generator/install/templates/lib/utils/bean_proxy.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/bean_proxy.rb.tt
new file mode 100644
index 0000000..770bd42
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/bean_proxy.rb.tt
@@ -0,0 +1,15 @@
+class BeanProxy
+ def initialize(bean_name:)
+ self.bean_name = bean_name
+ end
+
+ def bean
+ App::Container.resolve(bean_name)
+ end
+
+ delegate_missing_to :bean
+
+ private
+
+ attr_accessor :bean_name
+end
diff --git a/lib/dry_module_generator/install/templates/lib/utils/command_serializer.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/command_serializer.rb.tt
new file mode 100644
index 0000000..f761509
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/command_serializer.rb.tt
@@ -0,0 +1,15 @@
+require_relative 'application_struct'
+
+class CommandSerializer < ActiveJob::Serializers::ObjectSerializer
+ def serialize(command)
+ YAML.dump(command)
+ end
+
+ def deserialize(command)
+ YAML.load(command)
+ end
+
+ def serialize?(command)
+ command.is_a? ApplicationStruct
+ end
+end
diff --git a/lib/dry_module_generator/install/templates/lib/utils/concurrent/async_job_scheduler.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/concurrent/async_job_scheduler.rb.tt
new file mode 100644
index 0000000..c523d2b
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/concurrent/async_job_scheduler.rb.tt
@@ -0,0 +1,42 @@
+class AsyncJobScheduler
+ def initialize(executor_service:)
+ self.executor_service = executor_service
+ end
+
+ def schedule(callable = nil, delay: 0.seconds, &block)
+ unless !!callable ^ !!block
+ raise ArgumentError, 'Either pass in a proc, or a block, but not both or none'
+ end
+
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ future = Concurrent::Promises.delay_on(executor_service) do
+ Rails.application.executor.wrap do
+ sleep(delay)
+ (callable || block).call
+ end
+ end
+
+ future.on_rejection_using(executor_service) { |reason| raise StandardError.new(reason) }.touch
+ end
+ end
+
+ def fulfilled_future(result = nil)
+ Concurrent::Promises.fulfilled_future(result, executor_service)
+ end
+
+ def rejected_future(error)
+ Concurrent::Promises.rejected_future(error)
+ end
+
+ def wrap
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ Rails.application.executor.wrap do
+ yield
+ end
+ end
+ end
+
+ private
+
+ attr_accessor :executor_service
+end
diff --git a/lib/dry_module_generator/install/templates/utils/contract_validator.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/contract_validator.rb.tt
similarity index 78%
rename from lib/dry_module_generator/install/templates/utils/contract_validator.rb.tt
rename to lib/dry_module_generator/install/templates/lib/utils/contract_validator.rb.tt
index 68aed8b..e1a7ebb 100644
--- a/lib/dry_module_generator/install/templates/utils/contract_validator.rb.tt
+++ b/lib/dry_module_generator/install/templates/lib/utils/contract_validator.rb.tt
@@ -1,8 +1,7 @@
class ContractValidator
def validate(input_hash, validator, options = {})
+ validator.params = input_hash
result = validator.call(input_hash)
- validator.result = result
- validator.params = result.to_h.with_indifferent_access
return validator.class.command.new(result.to_h.merge(options)) if result.success?
diff --git a/lib/dry_module_generator/install/templates/lib/utils/events/active_job_event_scheduler.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/events/active_job_event_scheduler.rb.tt
new file mode 100644
index 0000000..8a2310b
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/events/active_job_event_scheduler.rb.tt
@@ -0,0 +1,5 @@
+class ActiveJobEventScheduler < RailsEventStore::ActiveJobScheduler
+ def call(klass, serialized_event)
+ klass.perform_later(serialized_event)
+ end
+end
diff --git a/lib/dry_module_generator/install/templates/lib/utils/events/async_thread_event_scheduler.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/events/async_thread_event_scheduler.rb.tt
new file mode 100644
index 0000000..c2a008a
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/events/async_thread_event_scheduler.rb.tt
@@ -0,0 +1,25 @@
+require 'utils/optional'
+
+class AsyncThreadEventScheduler
+ def initialize(job_scheduler: nil, event_deserializer:)
+ self.job_scheduler = job_scheduler
+ self.event_deserializer = event_deserializer
+ end
+
+ def call(subscriber, event)
+ deserialized = event_deserializer.load(event)
+ sub = Optional.of(subscriber)
+ .filter { |it| Class === it }
+ .map(&:new)
+ .or_else(subscriber)
+ job_scheduler.schedule(-> { sub.call(deserialized) })
+ end
+
+ def verify(subscriber)
+ (Class === subscriber && subscriber < AsyncThreadEventHandler) || (Method === subscriber)
+ end
+
+ private
+
+ attr_accessor :job_scheduler, :event_deserializer
+end
diff --git a/lib/dry_module_generator/install/templates/lib/utils/events/immediate_active_job_event_scheduler.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/events/immediate_active_job_event_scheduler.rb.tt
new file mode 100644
index 0000000..bbd83cf
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/events/immediate_active_job_event_scheduler.rb.tt
@@ -0,0 +1,5 @@
+class ImmediateActiveJobEventScheduler < RailsEventStore::ActiveJobScheduler
+ def call(klass, serialized_event)
+ klass.perform_now(serialized_event)
+ end
+end
diff --git a/lib/dry_module_generator/install/templates/lib/utils/events/immediate_event_scheduler.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/events/immediate_event_scheduler.rb.tt
new file mode 100644
index 0000000..9dd54f2
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/events/immediate_event_scheduler.rb.tt
@@ -0,0 +1,16 @@
+require_relative 'async_thread_event_scheduler'
+
+class ImmediateEventScheduler < AsyncThreadEventScheduler
+ def initialize(event_deserializer:)
+ self.event_deserializer = event_deserializer
+ end
+
+ def call(subscriber, event)
+ deserialized = event_deserializer.load(event)
+ sub = Optional.of(subscriber)
+ .filter { |it| Class === it }
+ .map(&:new)
+ .or_else(subscriber)
+ sub.call(deserialized)
+ end
+end
diff --git a/lib/dry_module_generator/install/templates/lib/utils/events/integration_event.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/events/integration_event.rb.tt
new file mode 100644
index 0000000..d130b8a
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/events/integration_event.rb.tt
@@ -0,0 +1,19 @@
+class IntegrationEvent < RailsEventStore::Event
+ def current_user_id
+ metadata[:user_id]
+ end
+
+ def registered_at
+ metadata[:registered_at]
+ end
+
+ def ==(other)
+ data == other.data && self.class == other.class
+ end
+
+ alias eql? ==
+
+ def hash
+ data.hash
+ end
+end
diff --git a/lib/dry_module_generator/install/templates/lib/utils/events/metadata_event_publisher.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/events/metadata_event_publisher.rb.tt
new file mode 100644
index 0000000..cb22e5e
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/events/metadata_event_publisher.rb.tt
@@ -0,0 +1,37 @@
+require 'utils/optional'
+
+class MetadataEventPublisher
+ attr_accessor :client, :current_user_repository
+
+ def initialize(
+ client: App::Container.resolve('events.client'),
+ current_user_repository: App::Container.resolve('current_user_repository')
+ )
+ self.client = client
+ self.current_user_repository = current_user_repository
+ end
+
+ def publish(event)
+ user_id = Optional.of_nullable(current_user_repository.authenticated_identity).map(&:id).or_else(nil)
+ client.with_metadata({ user_id: user_id, registered_at: DateTime.now }) do
+ client.publish(event)
+ end
+ end
+
+ def publish_all(events)
+ events.each { |event| publish(event) }
+ events.clear
+ end
+
+ def subscribe(subscriber, to:)
+ client.subscribe(subscriber, to: to)
+ end
+
+ def mapper
+ client.send(:mapper)
+ end
+
+ def deserialize(serialized_event, serializer)
+ serializer.load(serialized_event)
+ end
+end
diff --git a/lib/dry_module_generator/install/templates/lib/utils/events/no_op_event_repository.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/events/no_op_event_repository.rb.tt
new file mode 100644
index 0000000..e2fdc60
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/events/no_op_event_repository.rb.tt
@@ -0,0 +1,42 @@
+class NoOpEventRepository
+ def add_to_stream(*_args)
+ self
+ end
+
+ def link_to_stream(*_args)
+ self
+ end
+
+ def delete_stream(*_args)
+ self
+ end
+
+ def has_event?(_event_id)
+ false
+ end
+
+ def last_stream_event(_stream)
+ nil
+ end
+
+ def read(_specification)
+ nil
+ end
+
+ def count(_specification)
+ 0
+ end
+
+ def update_messages(_messages)
+ self
+ end
+
+ def streams_of(_event_id)
+ []
+ end
+
+ def append_to_stream(*_args)
+ self
+ end
+end
+
diff --git a/lib/dry_module_generator/install/templates/lib/utils/events/to_yaml_event_mapper.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/events/to_yaml_event_mapper.rb.tt
new file mode 100644
index 0000000..1c7d5c3
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/events/to_yaml_event_mapper.rb.tt
@@ -0,0 +1,13 @@
+require 'ruby_event_store/mappers/pipeline_mapper'
+
+class ToYAMLEventMapper < RubyEventStore::Mappers::PipelineMapper
+ attr_reader :pipeline
+
+ def initialize(pipeline: YAML)
+ self.pipeline = pipeline
+ end
+
+ private
+
+ attr_writer :pipeline
+end
diff --git a/lib/dry_module_generator/install/templates/lib/utils/injection/active_job_strategy.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/injection/active_job_strategy.rb.tt
new file mode 100644
index 0000000..ec88bcf
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/injection/active_job_strategy.rb.tt
@@ -0,0 +1,48 @@
+require 'utils/bean_proxy'
+
+module Dry
+ module AutoInject
+ class Strategies
+ class ActiveJobStrategy < Kwargs
+ private
+
+ def define_new
+ class_mod.class_exec(container, dependency_map) do |_container, dependency_map|
+ map = dependency_map.to_h
+
+ define_method :new do |*args, **kwargs|
+ map.each do |name, identifier|
+ kwargs[name] = BeanProxy.new(bean_name: identifier) unless kwargs.key?(name)
+ end
+
+ super(*args, **kwargs)
+ end
+ end
+ end
+
+ def define_initialize_with_splat(super_parameters)
+ assign_dependencies = method(:assign_dependencies)
+ slice_kwargs = method(:slice_kwargs)
+
+ instance_mod.class_exec do
+ define_method :initialize do |*args, **kwargs|
+ assign_dependencies.(kwargs, self)
+
+ if super_parameters.splat?
+ super(*args)
+ else
+ super_kwargs = slice_kwargs.(kwargs, super_parameters)
+
+ if super_kwargs.any?
+ super(*args, super_kwargs)
+ else
+ super(*args)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/dry_module_generator/install/templates/utils/injection/controller_resolve_strategy.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/injection/controller_resolve_strategy.rb.tt
similarity index 100%
rename from lib/dry_module_generator/install/templates/utils/injection/controller_resolve_strategy.rb.tt
rename to lib/dry_module_generator/install/templates/lib/utils/injection/controller_resolve_strategy.rb.tt
diff --git a/lib/dry_module_generator/install/templates/lib/utils/list_query.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/list_query.rb.tt
new file mode 100644
index 0000000..9f8e3b0
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/list_query.rb.tt
@@ -0,0 +1,5 @@
+class ListQuery < ApplicationStruct
+ attribute? :q, Types::Any.default({}.freeze)
+ attribute? :page, Types::Coercible::Integer.default(1.freeze)
+ attribute? :page_size, Types::Coercible::Integer.default(Pagy::DEFAULT[:items].freeze)
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/install/templates/lib/utils/optional.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/optional.rb.tt
new file mode 100644
index 0000000..ed81154
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/optional.rb.tt
@@ -0,0 +1,97 @@
+class Optional
+ def self.of_nullable(value)
+ new(value)
+ end
+
+ def self.of(value)
+ raise ArgumentError, 'value is nil' if value.nil?
+ new(value)
+ end
+
+ def self.empty
+ new(nil)
+ end
+
+ def initialize(value)
+ self.value = value
+ end
+
+ def get
+ if value.nil?
+ raise ArgumentError, 'no value present'
+ else
+ value
+ end
+ end
+
+ def present?
+ !value.nil?
+ end
+
+ def if_present(&consumer)
+ consumer.call(value) if present?
+ end
+
+ def and_then(&consumer)
+ return self unless present?
+
+ consumer.call(value)
+ self
+ end
+
+ def filter(&predicate)
+ return self unless present?
+
+ predicate.call(value) ? self : Optional.empty
+ end
+
+ def map(&mapper)
+ return self unless present?
+
+ Optional.of_nullable(mapper.call(value))
+ end
+
+ def flat_map(&mapper)
+ return self unless present?
+
+ mapper.call(value)
+ end
+
+ def or(&supplier)
+ present? ? self : self.class.of_nullable(supplier.call)
+ end
+
+ def or_else(other)
+ present? ? value : other
+ end
+
+ def or_else_get(&supplier)
+ present? ? value : supplier.call
+ end
+
+ def or_else_raise(&error_supplier)
+ if present?
+ value
+ else
+ raise error_supplier.call
+ end
+ end
+
+ def as_json(_options = {})
+ present? ? value.as_json : nil
+ end
+
+ def ==(other)
+ other.filter { |it| it == value }.present?
+ end
+
+ alias eql? ==
+
+ def hash
+ value.hash
+ end
+
+ private
+
+ attr_accessor :value
+end
diff --git a/lib/dry_module_generator/install/templates/lib/utils/pagination_dto.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/pagination_dto.rb.tt
new file mode 100644
index 0000000..bb84fe3
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/pagination_dto.rb.tt
@@ -0,0 +1,9 @@
+require_relative 'application_read_struct'
+
+class PaginationDto < ApplicationReadStruct
+ attribute :count, Types::Integer
+ attribute :last, Types::Integer
+ attribute :next, Types::Integer
+ attribute :page, Types::Integer
+ attribute :pages, Types::Integer
+end
diff --git a/lib/dry_module_generator/install/templates/lib/utils/pagination_list_dto.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/pagination_list_dto.rb.tt
new file mode 100644
index 0000000..021c1ac
--- /dev/null
+++ b/lib/dry_module_generator/install/templates/lib/utils/pagination_list_dto.rb.tt
@@ -0,0 +1,6 @@
+require_relative 'application_read_struct'
+
+class PaginationListDto < ApplicationReadStruct
+ attribute :pagination, Types::Any
+ attribute :data, Types::Array
+end
diff --git a/lib/dry_module_generator/install/templates/utils/types.rb.tt b/lib/dry_module_generator/install/templates/lib/utils/types.rb.tt
similarity index 100%
rename from lib/dry_module_generator/install/templates/utils/types.rb.tt
rename to lib/dry_module_generator/install/templates/lib/utils/types.rb.tt
diff --git a/lib/dry_module_generator/install/templates/services/application_service.rb.tt b/lib/dry_module_generator/install/templates/services/application_service.rb.tt
deleted file mode 100644
index 3a8510f..0000000
--- a/lib/dry_module_generator/install/templates/services/application_service.rb.tt
+++ /dev/null
@@ -1,5 +0,0 @@
-class ApplicationService
- def map_into(data, mapper, options = {})
- DryObjectMapper::Mapper.call(data, mapper, options)
- end
-end
diff --git a/lib/dry_module_generator/install/templates/utils/application_contract.rb.tt b/lib/dry_module_generator/install/templates/utils/application_contract.rb.tt
deleted file mode 100644
index 0c454bc..0000000
--- a/lib/dry_module_generator/install/templates/utils/application_contract.rb.tt
+++ /dev/null
@@ -1,32 +0,0 @@
-class ApplicationContract < Dry::Validation::Contract
- attr_accessor :errors, :params, :result
-
- def initialize(errors: {}, params: {}, result: nil)
- super()
- self.errors = errors
- self.params = params
- self.result = result
- end
-
- def self.command
- DryStructGenerator::StructGenerator.new.call(self)
- end
-
- register_macro(:email_format) do
- unless /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i.match?(value)
- key.failure('not a valid email')
- end
- end
-
- register_macro(:password_format) do
- unless /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/i.match?(value)
- key.failure('must contain 1 lower case letter, 1 upper case letter, 1 number, 1 special character and have a minimum length of 8')
- end
- end
-
- def to_h
- self.class.schema.rules.keys.inject({}) do |hash, key|
- hash.merge({ key => self.send(key) })
- end
- end
-end
diff --git a/lib/dry_module_generator/module/generator.rb b/lib/dry_module_generator/module/generator.rb
index 5e06b27..ef06fe3 100644
--- a/lib/dry_module_generator/module/generator.rb
+++ b/lib/dry_module_generator/module/generator.rb
@@ -30,19 +30,41 @@ class Generator < Rails::Generators::NamedBase
'boolean': "check_box_tag"
}.freeze
+ attr_accessor :config
+
def initialize(args, *options)
super
self.module_name = args[0]
+ self.config = Config::GeneratorConfiguration
end
def create_app
template("app/service.rb", File.join("#{module_path}/app/#{class_name.downcase}_service.rb"))
- template("app/list_dto.rb", File.join("#{module_path}/app/get_#{class_name.pluralize.downcase}_list_dto.rb"))
- template("app/details_dto.rb", File.join("#{module_path}/app/get_#{class_name.downcase}_details_dto.rb"))
+ template("app/read_service/service.rb", File.join("#{module_path}/app/read_service/#{class_name.downcase}_service.rb"))
+ template(
+ "app/read_service/get_list_dto.rb",
+ File.join("#{module_path}/app/read_service/get_#{class_name.pluralize.downcase}_list_dto.rb")
+ )
+ template(
+ "app/read_service/get_details_dto.rb",
+ File.join("#{module_path}/app/read_service/get_#{class_name.downcase}_details_dto.rb")
+ )
end
def create_domain
template("domain/model.rb", File.join("#{module_path}/domain/#{class_name.downcase}.rb"))
+ template(
+ "domain/events/created_event.rb",
+ File.join("#{module_path}/domain/#{class_name.downcase}/created_event.rb")
+ )
+ template(
+ "domain/events/updated_event.rb",
+ File.join("#{module_path}/domain/#{class_name.downcase}/updated_event.rb")
+ )
+ template(
+ "domain/events/deleted_event.rb",
+ File.join("#{module_path}/domain/#{class_name.downcase}/deleted_event.rb")
+ )
end
def create_infra
@@ -56,43 +78,64 @@ def create_infra
)
template("infra/config/application.rb", File.join("#{module_path}/infra/config/application.rb"))
template("infra/config/routes.rb", File.join("#{module_path}/infra/config/routes.rb"))
+ template("infra/config/importmap.rb", File.join("#{module_path}/infra/config/importmap.rb"))
end
def create_ui
- @action_type = "Create"
- template("ui/validation.rb", File.join("#{module_path}/ui/create_#{class_name.downcase}_validator.rb"))
- @action_type = "Update"
- template("ui/validation.rb", File.join("#{module_path}/ui/update_#{class_name.downcase}_validator.rb"))
+ template("ui/create_validation.rb", File.join("#{module_path}/ui/create_#{class_name.downcase}_validator.rb"))
+ template("ui/update_validation.rb", File.join("#{module_path}/ui/update_#{class_name.downcase}_validator.rb"))
template("ui/controller.rb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}_controller.rb"))
+ empty_directory("#{module_path}/ui/javascript/controllers") if Config::GeneratorConfiguration.include_views
end
def create_views
- template("ui/views/form.rb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}/_form.html.erb"))
- template("ui/views/index.rb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}/index.html.erb"))
- template("ui/views/show.rb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}/show.html.erb"))
- template("ui/views/new.rb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}/new.html.erb"))
- template("ui/views/edit.rb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}/edit.html.erb"))
+ return unless Config::GeneratorConfiguration.include_views
+ template_path = "ui/views/#{config.css_framework}/#{config.html_style}"
+ template("#{template_path}/_form.html.erb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}/_form.html.erb"))
+ template("#{template_path}/_table_filter.html.erb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}/_table_filter.html.erb"))
+ template("#{template_path}/index.html.erb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}/index.html.erb"))
+ template("#{template_path}/show.html.erb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}/show.html.erb"))
+ template("#{template_path}/new.html.erb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}/new.html.erb"))
+ template("#{template_path}/edit.html.erb", File.join("#{module_path}/ui/#{class_name.pluralize.downcase}/edit.html.erb"))
+ end
+
+ def add_importmap_configuration
+ append_to_file("app/assets/config/manifest.js") do
+ "//= link_tree ../../../#{module_name}/lib/#{module_name}/ui/javascript/controllers .js"
+ end
+
+ append_to_file("app/javascript/controllers/index.js") do
+ "eagerLoadControllersFrom('#{module_name}', application)"
+ end
end
def create_tests
- @action_type = "Create"
template(
- "spec/ui/validation_test.rb",
- File.join("#{module_name}/spec/ui/create_#{class_name.downcase}_validator_spec.rb")
+ "spec/app/service_spec.rb",
+ File.join("#{module_name}/spec/app/#{class_name.downcase}_service_spec.rb")
)
- @action_type = "Update"
template(
- "spec/ui/validation_test.rb",
- File.join("#{module_name}/spec/ui/update_#{class_name.downcase}_validator_spec.rb")
+ "spec/app/read_service/service_spec.rb",
+ File.join("#{module_name}/spec/app/read_service/#{class_name.downcase}_service_spec.rb")
)
template(
- "spec/app/service_test.rb",
- File.join("#{module_name}/spec/app/#{class_name.downcase}_service_spec.rb")
+ "spec/domain/model_spec.rb",
+ File.join("#{module_name}/spec/domain/#{class_name.downcase}_spec.rb")
)
template(
- "spec/ui/controller_test.rb",
+ "spec/ui/controller_spec.rb",
File.join("#{module_name}/spec/ui/#{class_name.pluralize.downcase}_controller_spec.rb")
)
+ @action_type = "Create"
+ template(
+ "spec/ui/validation_spec.rb",
+ File.join("#{module_name}/spec/ui/create_#{class_name.downcase}_validator_spec.rb")
+ )
+ @action_type = "Update"
+ template(
+ "spec/ui/validation_spec.rb",
+ File.join("#{module_name}/spec/ui/update_#{class_name.downcase}_validator_spec.rb")
+ )
end
private
@@ -178,4 +221,4 @@ def form_attributes
d
end
end
-end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/app/details_dto.rb.tt b/lib/dry_module_generator/module/templates/app/details_dto.rb.tt
deleted file mode 100644
index 8400ef0..0000000
--- a/lib/dry_module_generator/module/templates/app/details_dto.rb.tt
+++ /dev/null
@@ -1,10 +0,0 @@
-<% module_namespacing do -%>
-module <%= module_name.capitalize %>
- module App
- class Get<%= class_name %>DetailsDto < ApplicationReadStruct
- attribute :id, Types::String
- <%= dto_definition.join("\n ") %>
- end
- end
-end
-<% end %>
diff --git a/lib/dry_module_generator/module/templates/app/list_dto.rb.tt b/lib/dry_module_generator/module/templates/app/list_dto.rb.tt
deleted file mode 100644
index 937b102..0000000
--- a/lib/dry_module_generator/module/templates/app/list_dto.rb.tt
+++ /dev/null
@@ -1,10 +0,0 @@
-<% module_namespacing do -%>
-module <%= module_name.capitalize %>
- module App
- class Get<%= class_name.pluralize %>ListDto < ApplicationReadStruct
- attribute :id, Types::String
- <%= dto_definition.join("\n ") %>
- end
- end
-end
-<% end %>
diff --git a/lib/dry_module_generator/module/templates/app/read_service/get_details_dto.rb.tt b/lib/dry_module_generator/module/templates/app/read_service/get_details_dto.rb.tt
new file mode 100644
index 0000000..aa44be5
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/app/read_service/get_details_dto.rb.tt
@@ -0,0 +1,10 @@
+module <%= module_name.capitalize %>
+ module App
+ module ReadService
+ class Get<%= class_name %>DetailsDto < ApplicationReadStruct
+ attribute :id, Types::String
+ <%= dto_definition.join("\n ") %>
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/app/read_service/get_list_dto.rb.tt b/lib/dry_module_generator/module/templates/app/read_service/get_list_dto.rb.tt
new file mode 100644
index 0000000..f66bf36
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/app/read_service/get_list_dto.rb.tt
@@ -0,0 +1,10 @@
+module <%= module_name.capitalize %>
+ module App
+ module ReadService
+ class Get<%= class_name.pluralize %>ListDto < ApplicationReadStruct
+ attribute :id, Types::String
+ <%= dto_definition.join("\n ") %>
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/app/read_service/service.rb.tt b/lib/dry_module_generator/module/templates/app/read_service/service.rb.tt
new file mode 100644
index 0000000..a1ac804
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/app/read_service/service.rb.tt
@@ -0,0 +1,23 @@
+module <%= module_name.capitalize %>
+ module App
+ module ReadService
+ class <%= class_name %>Service < ApplicationService
+ include Import[<%= class_name.downcase %>_repository: "<%= module_name %>.<%= class_name.downcase %>_repository"]
+
+ def get_all_<%= class_name.pluralize.downcase %>(query)
+ paginate_collection(
+ collection: <%= class_name.downcase %>_repository.all,
+ mapper: Get<%= class_name.pluralize %>ListDto,
+ page: query.page,
+ page_size: query.page_size,
+ filter: query.q
+ )
+ end
+
+ def get_<%= class_name.downcase %>(id)
+ map_into(<%= class_name.downcase %>_repository.find(id), Get<%= class_name %>DetailsDto)
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/app/service.rb.tt b/lib/dry_module_generator/module/templates/app/service.rb.tt
index 41c69f9..fd1241a 100644
--- a/lib/dry_module_generator/module/templates/app/service.rb.tt
+++ b/lib/dry_module_generator/module/templates/app/service.rb.tt
@@ -1,21 +1,29 @@
-<% module_namespacing do -%>
module <%= module_name.capitalize %>
module App
class <%= class_name %>Service < ApplicationService
include Import[<%= class_name.downcase %>_repository: "<%= module_name %>.<%= class_name.downcase %>_repository"]
def create_<%= class_name.downcase %>(command)
- result = ActiveRecord::Base.transaction do
- <%= class_name.downcase %>_repository.create!(command.to_h)
+ <%= class_name.downcase %> = ActiveRecord::Base.transaction do
+ <%= class_name.downcase %> = <%= class_name.downcase %>_repository.create_new(<% options[:attributes].each do |field_name, _| %>
+ <%= field_name %>: command.<%= field_name %>,<% end %>
+ )
+ <%= class_name.downcase %>_repository.save!(<%= class_name.downcase %>)
end
+
+ event_publisher.publish_all(<%= class_name.downcase %>.domain_events)
end
def update_<%= class_name.downcase %>(command)
- result = ActiveRecord::Base.transaction do
+ <%= class_name.downcase %> = ActiveRecord::Base.transaction do
<%= class_name.downcase %> = <%= class_name.downcase %>_repository.find(command.id)
- <%= class_name.downcase %>.update!(command.to_h)
- <%= class_name.downcase %>
+ <%= class_name.downcase %>.update_<%= class_name.downcase %>(<% options[:attributes].each do |field_name, _| %>
+ <%= field_name %>: command.<%= field_name %>,<% end %>
+ )
+ <%= class_name.downcase %>_repository.save!(<%= class_name.downcase %>)
end
+
+ event_publisher.publish_all(<%= class_name.downcase %>.domain_events)
end
def delete_<%= class_name.downcase %>(id)
@@ -23,15 +31,6 @@ module <%= module_name.capitalize %>
<%= class_name.downcase %>_repository.delete!(<%= class_name.downcase %>_repository.find(id))
end
end
-
- def get_all_<%= class_name.pluralize.downcase %>
- map_into(<%= class_name.downcase %>_repository.all, Get<%= class_name.pluralize %>ListDto)
- end
-
- def get_<%= class_name.downcase %>(id)
- map_into(<%= class_name.downcase %>_repository.find(id), Get<%= class_name %>DetailsDto)
- end
end
end
-end
-<% end -%>
\ No newline at end of file
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/domain/events/created_event.rb.tt b/lib/dry_module_generator/module/templates/domain/events/created_event.rb.tt
new file mode 100644
index 0000000..22724fb
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/domain/events/created_event.rb.tt
@@ -0,0 +1,16 @@
+module <%= module_name.capitalize %>
+ module Domain
+ class <%= class_name %>
+ class CreatedEvent < IntegrationEvent
+ def id
+ data[:id]
+ end
+ <% options[:attributes].each do |field_name, _| %>
+ def <%= field_name %>
+ data[:<%= field_name %>]
+ end
+ <% end %>
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/domain/events/deleted_event.rb.tt b/lib/dry_module_generator/module/templates/domain/events/deleted_event.rb.tt
new file mode 100644
index 0000000..44d10a9
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/domain/events/deleted_event.rb.tt
@@ -0,0 +1,16 @@
+module <%= module_name.capitalize %>
+ module Domain
+ class <%= class_name %>
+ class DeletedEvent < IntegrationEvent
+ def id
+ data[:id]
+ end
+ <% options[:attributes].each do |field_name, _| %>
+ def <%= field_name %>
+ data[:<%= field_name %>]
+ end
+ <% end %>
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/domain/events/updated_event.rb.tt b/lib/dry_module_generator/module/templates/domain/events/updated_event.rb.tt
new file mode 100644
index 0000000..07abf81
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/domain/events/updated_event.rb.tt
@@ -0,0 +1,16 @@
+module <%= module_name.capitalize %>
+ module Domain
+ class <%= class_name %>
+ class UpdatedEvent < IntegrationEvent
+ def id
+ data[:id]
+ end
+ <% options[:attributes].each do |field_name, _| %>
+ def <%= field_name %>
+ data[:<%= field_name %>]
+ end
+ <% end %>
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/domain/model.rb.tt b/lib/dry_module_generator/module/templates/domain/model.rb.tt
index a53a85d..7f7a327 100644
--- a/lib/dry_module_generator/module/templates/domain/model.rb.tt
+++ b/lib/dry_module_generator/module/templates/domain/model.rb.tt
@@ -1,9 +1,28 @@
-<% module_namespacing do -%>
module <%= module_name.capitalize %>
module Domain
- class <%= class_name %> < ApplicationRecord
+ class <%= class_name %> < AggregateRoot
self.table_name = "<%= class_name.pluralize.downcase %>"
+
+ def self.create_new(id: SecureRandom.uuid, <%= options[:attributes].map { |field_name, _| "#{field_name}:" }.join(', ') %>)
+ <%= class_name.downcase %> = new(
+ id: id,
+ <%= options[:attributes].map do |field_name, _|
+ "#{field_name}: #{field_name},"
+ end.join("\n ") %>
+ )
+
+ <%= class_name.downcase %>
+ end
+
+ def update_<%= class_name.downcase %>(<%= options[:attributes].map { |field_name, _| "#{field_name}:" }.join(', ') %>)
+ assign_attributes(
+ <%= options[:attributes].map do |field_name, _|
+ "#{field_name}: #{field_name},"
+ end.join("\n ") %>
+ )
+
+ self
+ end
end
end
-end
-<% end %>
\ No newline at end of file
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/infra/config/application.rb.tt b/lib/dry_module_generator/module/templates/infra/config/application.rb.tt
index 76d3e5f..a6eac11 100644
--- a/lib/dry_module_generator/module/templates/infra/config/application.rb.tt
+++ b/lib/dry_module_generator/module/templates/infra/config/application.rb.tt
@@ -1,5 +1,5 @@
-<% module_namespacing do -%>
-<%= Rails.application.class %>.config.paths.add '<%= module_name %>/lib', load_path: true, autoload: true
-<%= Rails.application.class %>.config.paths['db/migrate'] << '<%= module_path %>/infra/db/migrate'
-<%= Rails.application.class %>.config.paths['app/views'] << '<%= module_name %>/lib'
-<% end %>
\ No newline at end of file
+Rails.application.config.paths.add '<%= module_name %>/lib', load_path: true, autoload: true
+Rails.application.config.paths['db/migrate'] << '<%= module_path %>/infra/db/migrate'
+Rails.application.config.paths['app/views'] << '<%= module_name %>/lib'
+Rails.application.config.importmap.paths << Rails.root.join('<%= module_name %>/lib/<%= module_name %>/infra/config/importmap.rb')
+Rails.application.config.assets.paths << Rails.root.join("<%= module_name %>/lib/<%= module_name %>/ui/javascript/controllers")
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/infra/config/importmap.rb.tt b/lib/dry_module_generator/module/templates/infra/config/importmap.rb.tt
new file mode 100644
index 0000000..9b9bf43
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/infra/config/importmap.rb.tt
@@ -0,0 +1,5 @@
+controllers_path = Rails.root.join("<%= module_path %>/ui/javascript/controllers")
+controllers_path.glob("**/*_controller.js").each do |controller|
+ name = controller.relative_path_from(controllers_path).to_s.remove(/\.js$/)
+ pin "<%= module_name %>/#{name}", to: name
+end
diff --git a/lib/dry_module_generator/module/templates/infra/config/routes.rb.tt b/lib/dry_module_generator/module/templates/infra/config/routes.rb.tt
index f65434d..6183dc2 100644
--- a/lib/dry_module_generator/module/templates/infra/config/routes.rb.tt
+++ b/lib/dry_module_generator/module/templates/infra/config/routes.rb.tt
@@ -1,5 +1,3 @@
-<% module_namespacing do -%>
Rails.application.routes.draw do
resources :<%= class_name.pluralize.downcase %>, controller: "<%= module_name %>/ui/<%= class_name.pluralize.downcase %>"
-end
-<% end %>
\ No newline at end of file
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/infra/db/migrate/migration.rb.tt b/lib/dry_module_generator/module/templates/infra/db/migrate/migration.rb.tt
index e914a72..972c46e 100644
--- a/lib/dry_module_generator/module/templates/infra/db/migrate/migration.rb.tt
+++ b/lib/dry_module_generator/module/templates/infra/db/migrate/migration.rb.tt
@@ -1,12 +1,11 @@
-<% module_namespacing do -%>
class Create<%= class_name.pluralize %> < ActiveRecord::Migration[<%= ActiveRecord::VERSION::MAJOR %>.<%= ActiveRecord::VERSION::MINOR %>]
def change
create_table :<%= class_name.pluralize.downcase %>, id: false do |t|
t.string :id, limit: 36, primary_key: true
<%= migration_definition.join("\n ") %>
+ t.boolean
t.timestamps
end
end
-end
-<% end %>
\ No newline at end of file
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/infra/system/provider_source.rb.tt b/lib/dry_module_generator/module/templates/infra/system/provider_source.rb.tt
index 4b4a8ac..fb4f352 100644
--- a/lib/dry_module_generator/module/templates/infra/system/provider_source.rb.tt
+++ b/lib/dry_module_generator/module/templates/infra/system/provider_source.rb.tt
@@ -1,11 +1,10 @@
-<% module_namespacing do -%>
Dry::System.register_provider_source(:<%= module_name %>, group: :<%= module_name %>) do
prepare do
register("<%= module_name %>.<%= class_name.downcase %>_repository") { <%= module_name.capitalize %>::Domain::<%= class_name %> }
register("<%= module_name %>.<%= class_name.downcase %>_service") { <%= module_name.capitalize %>::App::<%= class_name %>Service.new }
+ register("<%= module_name %>.<%= class_name.downcase %>_read_service") { <%= module_name.capitalize %>::App::ReadService::<%= class_name %>Service.new }
end
end
App::Container.register_provider(:<%= module_name %>, from: :<%= module_name %>)
-App::Container.start(:<%= module_name %>)
-<% end %>
+App::Container.start(:<%= module_name %>)
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/spec/app/read_service/service_spec.rb.tt b/lib/dry_module_generator/module/templates/spec/app/read_service/service_spec.rb.tt
new file mode 100644
index 0000000..bf7048e
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/spec/app/read_service/service_spec.rb.tt
@@ -0,0 +1,47 @@
+require 'rails_helper'
+
+RSpec.describe <%= module_name.capitalize %>::App::ReadService::<%= class_name %>Service, type: :unit do
+ subject(:<%= class_name.downcase %>_service) { described_class.new }
+
+ describe "#.get_all_<%= class_name.pluralize.downcase %>(query)" do
+ let(:query) { ListQuery.new(page: 1, page_size: 10, q: {}) }
+
+ context "when <%= class_name.downcase %> exists" do
+ let!(:<%= class_name.downcase %>) do
+ <%= module_name.capitalize %>::Domain::<%= class_name %>.create_new(<% options[:attributes].each do |field_name, _| %>
+ <%= field_name %>: "<%= field_name %>", <% end %>
+ ).tap(&:save!)
+ end
+
+ it "should return a PaginatedListDto as a result" do
+ result = <%= class_name.downcase %>_service.get_all_<%= class_name.pluralize.downcase %>(query)
+ expect(result.class).to eq(PaginationListDto)
+ expect(result.data.size).to eq(1)<% options[:attributes].each do |field_name, _| %>
+ expect(result.data[0].<%= field_name %>).to eq(<%= class_name.downcase %>.<%= field_name %>) <% end %>
+ end
+ end
+ end
+
+ describe "#.get_<%= class_name.downcase %>(id)" do
+ context "when the <%= class_name.downcase %> is not found" do
+ it "should raise an 404 not found error" do
+ expect { <%= class_name.downcase %>_service.get_<%= class_name.downcase %>(SecureRandom.uuid) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ context "when the <%= class_name.downcase %> is found" do
+ let!(:<%= class_name.downcase %>) do
+ <%= module_name.capitalize %>::Domain::<%= class_name %>.create_new(<% options[:attributes].each do |field_name, _| %>
+ <%= field_name %>: "<%= field_name %>", <% end %>
+ ).tap(&:save!)
+ end
+
+ it "should return a <%= class_name.downcase %> dto" do
+ result = <%= class_name.downcase %>_service.get_<%= class_name.downcase %>(<%= class_name.downcase %>.id)
+ expect(result.class).to eq(<%= module_name.capitalize %>::App::ReadService::Get<%= class_name %>DetailsDto)
+ expect(result.id).to eq(<%= class_name.downcase %>.id)<% options[:attributes].each do |field_name, _| %>
+ expect(result.<%= field_name %>).to eq(<%= class_name.downcase %>.<%= field_name %>)<% end %>
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/spec/app/service_test.rb.tt b/lib/dry_module_generator/module/templates/spec/app/service_spec.rb.tt
similarity index 74%
rename from lib/dry_module_generator/module/templates/spec/app/service_test.rb.tt
rename to lib/dry_module_generator/module/templates/spec/app/service_spec.rb.tt
index 8779334..ffd4f84 100644
--- a/lib/dry_module_generator/module/templates/spec/app/service_test.rb.tt
+++ b/lib/dry_module_generator/module/templates/spec/app/service_spec.rb.tt
@@ -2,23 +2,38 @@ require 'rails_helper'
RSpec.describe <%= module_name.capitalize %>::App::<%= class_name %>Service, type: :unit do
subject(:<%= class_name.downcase %>_service) do
- described_class.new(<%= class_name.downcase %>_repository: <%= class_name.downcase %>_repository)
+ described_class.new(
+ <%= class_name.downcase %>_repository: <%= class_name.downcase %>_repository,
+ event_publisher: event_publisher
+ )
end
let(:<%= class_name.downcase %>_repository) { spy(<%= module_name.capitalize %>::Domain::<%= class_name %>) }
+ let(:event_publisher) { spy(MetadataEventPublisher) }
describe "#.create_<%= class_name.downcase %>(command)" do
let(:command) do
<%= module_name.capitalize %>::Ui::Create<%= class_name %>Validator.command.new(<% contract_test_params.each do |param| %>
<%= param[:field_name] %>: <%= param[:value] %>,<% end %>
- id: SecureRandom.uuid
)
end
context "when creating a new <%= class_name.downcase %>" do
+ let(:<%= class_name.downcase %>) { instance_double(<%= module_name.capitalize %>::Domain::<%= class_name %>, domain_events: []) }
+
+ before do
+ allow(<%= class_name.downcase %>_repository).to receive(:create_new) { <%= class_name.downcase %> }
+ allow(<%= class_name.downcase %>_repository).to receive(:save!) { <%= class_name.downcase %> }
+ allow(event_publisher).to receive(:publish_all)
+ end
+
it 'should call all the required model methods' do
expect { <%= class_name.downcase %>_service.create_<%= class_name.downcase %>(command) }.to_not raise_error
- expect(<%= class_name.downcase %>_repository).to have_received(:create!).with(command.to_h)
+ expect(<%= class_name.downcase %>_repository).to have_received(:create_new).with(<% options[:attributes].each do |field_name, _| %>
+ <%= field_name %>: command.<%= field_name %>, <% end %>
+ )
+ expect(<%= class_name.downcase %>_repository).to have_received(:save!).with(<%= class_name.downcase %>)
+ expect(event_publisher).to have_received(:publish_all).with(<%= class_name.downcase %>.domain_events)
end
end
end
@@ -46,13 +61,15 @@ RSpec.describe <%= module_name.capitalize %>::App::<%= class_name %>Service, typ
before do
allow(<%= class_name.downcase %>_repository).to receive(:find) { <%= class_name.downcase %> }
- allow(<%= class_name.downcase %>).to receive(:update!)
+ allow(<%= class_name.downcase %>).to receive(:update_<%= class_name.downcase %>)
end
it 'should update the record' do
expect { <%= class_name.downcase %>_service.update_<%= class_name.downcase %>(command) }.to_not raise_error
expect(<%= class_name.downcase %>_repository).to have_received(:find).with(command.id)
- expect(<%= class_name.downcase %>).to have_received(:update!).with(command.to_h)
+ expect(<%= class_name.downcase %>).to have_received(:update_<%= class_name.downcase %>).with(<% options[:attributes].each do |field_name, _| %>
+ <%= field_name %>: command.<%= field_name %>, <% end %>
+ )
end
end
end
diff --git a/lib/dry_module_generator/module/templates/spec/domain/model_spec.rb.tt b/lib/dry_module_generator/module/templates/spec/domain/model_spec.rb.tt
new file mode 100644
index 0000000..4edbca0
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/spec/domain/model_spec.rb.tt
@@ -0,0 +1,37 @@
+require 'rails_helper'
+
+RSpec.describe <%= module_name.capitalize %>::Domain::<%= class_name %>, type: :unit do
+ describe "#self.create_new(id: SecureRandom.uuid, , <%= options[:attributes].map { |field_name, _| "#{field_name}:" }.join(', ') %>)" do
+ context "when creating a new <%= class_name.downcase %>" do
+ it "should save without error" do
+ id = SecureRandom.uuid <% options[:attributes].each do |field_name, _| %>
+ <%= field_name %> = "<%= field_name %>"<% end %>
+
+ <%= class_name.downcase %> = described_class.create_new(
+ id: id, <% options[:attributes].each do |field_name, _| %>
+ <%= field_name %>: <%= field_name %>, <% end %>
+ )
+
+ expect { described_class.save!(<%= class_name.downcase %>) }.to_not raise_error
+ end
+ end
+ end
+
+ describe "#.update_<%= class_name.downcase %>(<%= options[:attributes].map { |field_name, _| "#{field_name}:" }.join(', ') %>)" do
+ context "when updating an <%= class_name.downcase %>" do
+ it "should update the attributes" do
+ <%= class_name.downcase %> = described_class.create_new(<% options[:attributes].each do |field_name, _| %>
+ <%= field_name %>: "old_<%= field_name %>", <% end %>
+ )
+ <% options[:attributes].each do |field_name, _| %>
+ <%= field_name %> = "new_<%= field_name %>"<% end %>
+
+ <%= class_name.downcase %>.update_<%= class_name.downcase %>(<% options[:attributes].each do |field_name, _| %>
+ <%= field_name %>: <%= field_name %>, <% end %>
+ )
+ <% options[:attributes].each do |field_name, _| %>
+ expect(<%= class_name.downcase %>.<%= field_name %>).to eq(<%= field_name %>)<% end %>
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/spec/ui/controller_test.rb.tt b/lib/dry_module_generator/module/templates/spec/ui/controller_spec.rb.tt
similarity index 100%
rename from lib/dry_module_generator/module/templates/spec/ui/controller_test.rb.tt
rename to lib/dry_module_generator/module/templates/spec/ui/controller_spec.rb.tt
diff --git a/lib/dry_module_generator/module/templates/spec/ui/validation_test.rb.tt b/lib/dry_module_generator/module/templates/spec/ui/validation_spec.rb.tt
similarity index 100%
rename from lib/dry_module_generator/module/templates/spec/ui/validation_test.rb.tt
rename to lib/dry_module_generator/module/templates/spec/ui/validation_spec.rb.tt
diff --git a/lib/dry_module_generator/module/templates/ui/controller.rb.tt b/lib/dry_module_generator/module/templates/ui/controller.rb.tt
index abeeb35..d1d3a84 100644
--- a/lib/dry_module_generator/module/templates/ui/controller.rb.tt
+++ b/lib/dry_module_generator/module/templates/ui/controller.rb.tt
@@ -1,17 +1,26 @@
-<% module_namespacing do -%>
<%
controller_name = "#{class_name.pluralize}Controller"
service_name = "#{class_name.singularize.downcase}_service"
+ read_service_name = "#{class_name.singularize.downcase}_read_service"
pluralized_variable_name = class_name.pluralize.downcase
singularize_variable_name = class_name.singularize.downcase
view_path_name = class_name.pluralize.downcase
%>module <%= module_name.capitalize %>
module Ui
class <%= controller_name %> < ApplicationController
- include Import.inject[<%= service_name %>: "<%= module_name %>.<%= service_name %>"]
+ include Import.inject[
+ <%= service_name %>: "<%= module_name %>.<%= service_name %>",
+ <%= read_service_name %>: "<%= module_name %>.<%= read_service_name %>"
+ ]
def index
- @<%= pluralized_variable_name %> = <%= service_name %>.get_all_<%= pluralized_variable_name %>
+ @paginated_result = <%= read_service_name %>.get_all_<%= pluralized_variable_name %>(
+ ::ListQuery.new(
+ page: params[:page] || 1,
+ page_size: params[:page_size] || 10,
+ q: params[:q] || {}
+ )
+ )
end
def new
@@ -27,25 +36,31 @@
)
)
redirect_to <%= view_path_name %>_path, notice: "<%= class_name %> was successfully created!"
+ rescue ConstraintError => e
+ @form = e.validator
+ render :new, status: :unprocessable_entity
end
def show
- @<%= singularize_variable_name %> = <%= service_name %>.get_<%= singularize_variable_name %>(params[:id])
+ @<%= singularize_variable_name %> = <%= read_service_name %>.get_<%= singularize_variable_name %>(params[:id])
end
def edit
- @<%= singularize_variable_name %> = <%= service_name %>.get_<%= singularize_variable_name %>(params[:id])
+ @<%= singularize_variable_name %> = <%= read_service_name %>.get_<%= singularize_variable_name %>(params[:id])
@form = Update<%= class_name.singularize %>Validator.new(params: { <% options[:attributes].each do |field_name, value| %><%= field_name %>: @<%= singularize_variable_name %>.<%= field_name %>, <% end %> })
end
def update
<%= service_name %>.update_<%= singularize_variable_name %>(validator.validate(<%= singularize_variable_name %>_params, Update<%= class_name.singularize %>Validator.new, { id: params[:id] }))
- redirect_to <%= view_path_name %>_path, notice: "<%= class_name %> was successfully updated!"
+ redirect_to <%= view_path_name %>_path(page: params[:page], q: params[:q].as_json), notice: "<%= class_name %> was successfully updated!"
+ rescue ConstraintError => e
+ @form = e.validator
+ render :edit, status: :unprocessable_entity
end
def destroy
<%= service_name %>.delete_<%= singularize_variable_name %>(params[:id])
- redirect_to <%= view_path_name %>_path, notice: "<%= class_name %> was successfully deleted!"
+ redirect_to <%= view_path_name %>_path(page: params[:page], q: params[:q].as_json), notice: "<%= class_name %> was successfully deleted!"
end
private
@@ -55,5 +70,4 @@
end
end
end
-end
-<% end %>
\ No newline at end of file
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/create_validation.rb.tt b/lib/dry_module_generator/module/templates/ui/create_validation.rb.tt
new file mode 100644
index 0000000..692a60a
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/ui/create_validation.rb.tt
@@ -0,0 +1,9 @@
+module <%= module_name.capitalize %>
+ module Ui
+ class Create<%= class_name %>Validator < ApplicationContract
+ params do
+ <%= contract_definition.join("\n ") %>
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/validation.rb.tt b/lib/dry_module_generator/module/templates/ui/update_validation.rb.tt
similarity index 71%
rename from lib/dry_module_generator/module/templates/ui/validation.rb.tt
rename to lib/dry_module_generator/module/templates/ui/update_validation.rb.tt
index 2aa994a..ed9a983 100644
--- a/lib/dry_module_generator/module/templates/ui/validation.rb.tt
+++ b/lib/dry_module_generator/module/templates/ui/update_validation.rb.tt
@@ -1,7 +1,6 @@
-<% module_namespacing do -%>
module <%= module_name.capitalize %>
module Ui
- class <%= @action_type %><%= class_name %>Validator < ApplicationContract
+ class Update<%= class_name %>Validator < ApplicationContract
params do
<%= contract_definition.join("\n ") %>
end
@@ -11,5 +10,4 @@ module <%= module_name.capitalize %>
end
end
end
-end
-<% end %>
\ No newline at end of file
+end
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/beercss/erb/_form.html.erb.tt b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/_form.html.erb.tt
new file mode 100644
index 0000000..14e98b7
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/_form.html.erb.tt
@@ -0,0 +1,10 @@
+<%%= form_tag(url, method: method, id: 'form', "data-controller": 'form') do %>
+ <% form_attributes.each do |attribute| %>
+ <%%= <%= attribute[:type] %> '<%= class_name.downcase %>[<%= attribute[:field_name] %>]', @form.params.dig(:<%= attribute[:field_name] %>) %>
+ <%%= label_tag '<%= class_name.downcase %>[<%= attribute[:field_name] %>]', '<%= attribute[:label].humanize %>' %>
+ <%%= show_error(@form, :<%= attribute[:field_name] %>) %>
+
+ <% end %>
+ <%%= button_tag button_text %>
+ <%%= link_to 'Back', <%= class_name.pluralize.downcase %>_path(page: params[:page], q: params[:q].as_json), class: 'button border' %>
+<%% end %>
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/beercss/erb/_table_filter.html.erb.tt b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/_table_filter.html.erb.tt
new file mode 100644
index 0000000..aa9046b
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/_table_filter.html.erb.tt
@@ -0,0 +1,14 @@
+<%%= search_form_for <%= module_name.capitalize %>::Domain::<%= class_name %>.ransack(params[:q]), url: <%= class_name.pluralize.downcase %>_path, data: { "data-controller": "form" } do |f| %>
+ <% options[:attributes].each do |field_name, _| %>
+
+
+ <%%= f.search_field :<%= field_name %>_eq, placeholder: ' ' %>
+ <%%= f.label :<%= field_name %>_cont, '<%= field_name.capitalize.humanize %>' %>
+
+
<% end %>
+
+
+ <%%= button_tag 'Search', class: 'large no-margin responsive' %>
+
+
+<%% end %>
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/beercss/erb/edit.html.erb.tt b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/edit.html.erb.tt
new file mode 100644
index 0000000..28f9a3b
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/edit.html.erb.tt
@@ -0,0 +1,5 @@
+
+
Edit <%= class_name %>
+
+
+<%%= render 'form', url: <%= class_name.downcase %>_path(id: params[:id], page: params[:page], q: params[:q].as_json), method: :patch, button_text: 'Update' %>
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/beercss/erb/index.html.erb.tt b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/index.html.erb.tt
new file mode 100644
index 0000000..c578def
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/index.html.erb.tt
@@ -0,0 +1,48 @@
+
+
<%= class_name.pluralize %>
+ <%%= link_to new_<%= class_name.downcase %>_path(page: params[:page], q: params[:q].as_json), class: 'button extend circle' do %>
+ add
+ Add <%= class_name.singularize %>
+ <%% end %>
+
+
+<%%= render 'table_filter' %>
+
+<%%= turbo_frame_tag '<%= class_name.pluralize.downcase %>', "data-turbo-action": 'advance' do %>
+
+
+
+ <% options[:attributes].each do |field_name, _| %>
+ <%= field_name.capitalize.humanize %> <% end %>
+
+
+
+
+ <%% @paginated_result.data.each do |<%= class_name.downcase %>| %>
+ <% options[:attributes].each do |field_name, _| %>
+ <%%= <%= class_name.downcase %>.<%= field_name %> || '/' %>
<% end %>
+
+
+ <%%= link_to <%= class_name.downcase %>_path(id: <%= class_name.downcase %>.id, page: params[:page], q: params[:q].as_json), class: 'button border left-round max', "data-turbo-frame": "_top" do %>
+ visibility
+ <%% end %>
+
+ <%%= link_to edit_<%= class_name.downcase %>_path(id: <%= class_name.downcase %>.id, page: params[:page], q: params[:q].as_json), class: 'button border no-round max', "data-turbo-frame": "_top" do %>
+ edit
+ <%% end %>
+
+ <%%= link_to <%= class_name.downcase %>_path(id: <%= class_name.downcase %>.id, page: params[:page], q: params[:q].as_json), data: { "turbo-method": :delete }, class: 'button border right-round max' do %>
+ delete
+ <%% end %>
+
+
+
+ <%% end %>
+
+
+
+
+ <%% if @paginated_result.pagination.pages > 1 %>
+ <%%= render 'shared/pagination' %>
+ <%% end %>
+<%% end %>
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/beercss/erb/new.html.erb.tt b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/new.html.erb.tt
new file mode 100644
index 0000000..f60e00d
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/new.html.erb.tt
@@ -0,0 +1,5 @@
+
+
Add <%= class_name %>
+
+
+<%%= render 'form', url: <%= class_name.pluralize.downcase %>_path(page: params[:page], q: params[:q].as_json), method: :post, button_text: 'Save' %>
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/beercss/erb/show.html.erb.tt b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/show.html.erb.tt
new file mode 100644
index 0000000..e06a618
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/ui/views/beercss/erb/show.html.erb.tt
@@ -0,0 +1,16 @@
+
+
<%= class_name %> Details
+
+
+<% options[:attributes].each do |field_name, _| %>
+
+
<%= field_name.capitalize.humanize %>
+
+
+
<%%= @<%= class_name.downcase %>.<%= field_name %> || '/' %>
+
+
+
+
<% end %>
+
+<%%= link_to "Back to <%= class_name.pluralize.downcase %>", <%= class_name.pluralize.downcase %>_path(page: params[:page], q: params[:q].as_json), class: "button border large-margin", style: 'margin-left: 0 !important;' %>
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/form.rb.tt b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/_form.html.erb.tt
similarity index 94%
rename from lib/dry_module_generator/module/templates/ui/views/form.rb.tt
rename to lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/_form.html.erb.tt
index dec2e57..3119dc0 100644
--- a/lib/dry_module_generator/module/templates/ui/views/form.rb.tt
+++ b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/_form.html.erb.tt
@@ -1,4 +1,3 @@
-<% module_namespacing do -%>
<%%= form_tag(url, method: method, id: 'form', novalidate: true, "data-controller": 'form') do %>
<% form_attributes.each do |attribute| %>
<%%= <%= attribute[:type] %> '<%= class_name.downcase %>[<%= attribute[:field_name] %>]', @form.params.dig(:<%= attribute[:field_name] %>), class: 'form-control', placeholder: '<%= attribute[:field_name].capitalize.humanize %>', required: <%= attribute[:required] %> %>
@@ -8,5 +7,4 @@
<% end %>
<%%= button_tag button_text, class: 'btn btn-primary' %>
<%%= link_to "Back", <%= class_name.pluralize.downcase %>_path, class: "btn btn-danger" %>
-<%% end %>
-<% end %>
+<%% end %>
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/_table_filter.html.erb.tt b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/_table_filter.html.erb.tt
new file mode 100644
index 0000000..3cd3e48
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/_table_filter.html.erb.tt
@@ -0,0 +1,12 @@
+<%%= search_form_for <%= module_name.capitalize %>::Domain::<%= class_name %>.ransack(params[:q]), url: <%= class_name.pluralize.downcase %>_path, data: { "data-controller": "form" } do |f| %>
+
<% options[:attributes].each do |field_name, _| %>
+
+ <%%= f.label :<%= field_name %>_cont, '<%= field_name.capitalize.humanize %>', class: 'visually-hidden' %>
+ <%%= f.search_field :<%= field_name %>_eq, placeholder: '<%= field_name.capitalize %>', class: 'form-control' %>
+
<% end %>
+
+
+ <%%= button_tag 'Search', class: 'btn btn-primary' %>
+
+
+<%% end %>
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/edit.rb.tt b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/edit.html.erb.tt
similarity index 64%
rename from lib/dry_module_generator/module/templates/ui/views/edit.rb.tt
rename to lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/edit.html.erb.tt
index f3cd953..a1ebfa4 100644
--- a/lib/dry_module_generator/module/templates/ui/views/edit.rb.tt
+++ b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/edit.html.erb.tt
@@ -1,6 +1,4 @@
-<% module_namespacing do -%>
Edit <%= class_name %>
-<%%= render 'form', url: <%= class_name.downcase %>_path(id: params[:id]), method: :patch, button_text: 'Update' %>
-<% end %>
+<%%= render 'form', url: <%= class_name.downcase %>_path(id: params[:id]), method: :patch, button_text: 'Update' %>
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/index.rb.tt b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/index.html.erb.tt
similarity index 91%
rename from lib/dry_module_generator/module/templates/ui/views/index.rb.tt
rename to lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/index.html.erb.tt
index 8af22b3..a6da02f 100644
--- a/lib/dry_module_generator/module/templates/ui/views/index.rb.tt
+++ b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/index.html.erb.tt
@@ -1,4 +1,3 @@
-<% module_namespacing do -%>
<%= class_name.pluralize %>
@@ -9,6 +8,7 @@
+
@@ -19,7 +19,7 @@
- <%% @<%= class_name.pluralize.downcase %>.each do |<%= class_name.downcase %>| %>
+ <%% @paginated_result.data.each do |<%= class_name.downcase %>| %>
<% options[:attributes].each do |field_name, _| %>
<%%= <%= class_name.downcase %>.<%= field_name %> || '/' %> <% end %>
@@ -31,6 +31,4 @@
<%% end %>
-
-<% end %>
-
+
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/new.html.erb.tt b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/new.html.erb.tt
new file mode 100644
index 0000000..e418107
--- /dev/null
+++ b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/new.html.erb.tt
@@ -0,0 +1,4 @@
+
+ New <%= class_name %>
+
+<%%= render 'form', url: <%= class_name.pluralize.downcase %>_path, method: :post, button_text: 'Save' %>
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/show.rb.tt b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/show.html.erb.tt
similarity index 93%
rename from lib/dry_module_generator/module/templates/ui/views/show.rb.tt
rename to lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/show.html.erb.tt
index 47735c9..a8f9997 100644
--- a/lib/dry_module_generator/module/templates/ui/views/show.rb.tt
+++ b/lib/dry_module_generator/module/templates/ui/views/bootstrap5/erb/show.html.erb.tt
@@ -1,4 +1,3 @@
-<% module_namespacing do -%>
<%= class_name %> Details
@@ -12,5 +11,4 @@
<% end %>
-<%%= link_to "Back to <%= class_name.pluralize.downcase %>", <%= class_name.pluralize.downcase %>_path, class: "btn btn-danger" %>
-<% end %>
\ No newline at end of file
+<%%= link_to "Back to <%= class_name.pluralize.downcase %>", <%= class_name.pluralize.downcase %>_path, class: "btn btn-danger" %>
\ No newline at end of file
diff --git a/lib/dry_module_generator/module/templates/ui/views/new.rb.tt b/lib/dry_module_generator/module/templates/ui/views/new.rb.tt
deleted file mode 100644
index 1d5faec..0000000
--- a/lib/dry_module_generator/module/templates/ui/views/new.rb.tt
+++ /dev/null
@@ -1,7 +0,0 @@
-<% module_namespacing do -%>
-
- Add <%= class_name %>
-
-<%%= render 'form', url: <%= class_name.pluralize.downcase %>_path, method: :post, button_text: 'Save' %>
-<% end %>
-
diff --git a/lib/dry_module_generator/uninstall/uninstaller.rb b/lib/dry_module_generator/uninstall/uninstaller.rb
deleted file mode 100644
index 68a6623..0000000
--- a/lib/dry_module_generator/uninstall/uninstaller.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-module DryModuleGenerator
- class Uninstaller < Rails::Generators::Base
- source_root File.expand_path("templates", __dir__)
- namespace "dry_module:uninstall"
-
- # causing class errors
- def remove_config_files
- remove_file "config/initializers/container.rb"
- remove_file "config/initializers/dependency_injection.rb"
- remove_file "config/initializers/dry_struct_generator.rb"
- remove_file "config/initializers/routes.rb"
- end
-
- def remove_utility_files
- remove_file "lib/utils/types.rb"
- remove_file "lib/utils/application_struct.rb"
- remove_file "lib/utils/application_read_struct.rb"
- remove_file "lib/utils/application_contract.rb"
- remove_file "lib/utils/contract_validator.rb"
- FileUtils.remove_dir("lib/utils/injection", force: true)
- end
-
- def remove_error_file
- remove_file "app/errors/constraint_error.rb"
- end
-
- def remove_service_file
- remove_file "app/services/application_service.rb"
- end
- end
-end