Skip to content

Commit

Permalink
Made gem configurable, added both bootstrap5 and beercss views based …
Browse files Browse the repository at this point in the history
…on configuration, added read service and specs
  • Loading branch information
Jane-Terziev committed Feb 19, 2024
1 parent f766de1 commit 2b46d15
Show file tree
Hide file tree
Showing 82 changed files with 1,144 additions and 298 deletions.
5 changes: 3 additions & 2 deletions lib/dry_module_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
31 changes: 31 additions & 0 deletions lib/dry_module_generator/config/configuration.rb
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions lib/dry_module_generator/config/generator_configuration.rb
Original file line number Diff line number Diff line change
@@ -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
158 changes: 75 additions & 83 deletions lib/dry_module_generator/install/installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -87,29 +28,17 @@ 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

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)
Expand All @@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class CurrentUserRepository < ActiveSupport::CurrentAttributes
attribute :authenticated_identity
end
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Just a marker abstract class
class AsyncThreadEventHandler
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<%%
key = flash&.keys&.first&.to_s || ""
value = flash[key] || []
value = [value] unless value.is_a?(Array)
%>

<div id="flash"
data-controller="flash"
data-flash-type-value="<%%= key %>"
data-flash-message-value="<%%= value %>"
>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require 'utils/command_serializer'

Rails.application.config.active_job.custom_serializers << CommandSerializer
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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)
Loading

0 comments on commit 2b46d15

Please sign in to comment.