Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
xronos-i-am committed Dec 10, 2024
1 parent 724d017 commit 94ea2e1
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 84 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ gem 'rake', '~> 13.0'
gem 'rspec', '~> 3.0'

gem 'activesupport'
gem 'rails'
gem 'combustion'
gem 'rubocop-gp', github: 'corp-gp/rubocop-gp', require: false
88 changes: 88 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,66 @@ PATH
GEM
remote: https://rubygems.org/
specs:
actioncable (7.0.7)
actionpack (= 7.0.7)
activesupport (= 7.0.7)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.7)
actionpack (= 7.0.7)
activejob (= 7.0.7)
activerecord (= 7.0.7)
activestorage (= 7.0.7)
activesupport (= 7.0.7)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.7)
actionpack (= 7.0.7)
actionview (= 7.0.7)
activejob (= 7.0.7)
activesupport (= 7.0.7)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.7)
actionview (= 7.0.7)
activesupport (= 7.0.7)
rack (~> 2.0, >= 2.2.4)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.7)
actionpack (= 7.0.7)
activerecord (= 7.0.7)
activestorage (= 7.0.7)
activesupport (= 7.0.7)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.0.7)
activesupport (= 7.0.7)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (7.0.7)
activesupport (= 7.0.7)
globalid (>= 0.3.6)
activemodel (7.0.7)
activesupport (= 7.0.7)
activerecord (7.0.7)
activemodel (= 7.0.7)
activesupport (= 7.0.7)
activestorage (7.0.7)
actionpack (= 7.0.7)
activejob (= 7.0.7)
activerecord (= 7.0.7)
activesupport (= 7.0.7)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.7)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
Expand All @@ -44,6 +91,7 @@ GEM
thor (>= 0.14.6)
concurrent-ruby (1.2.2)
crass (1.0.6)
date (3.4.1)
diff-lcs (1.5.0)
dry-configurable (1.1.0)
dry-core (~> 1.0, < 2)
Expand All @@ -52,15 +100,36 @@ GEM
concurrent-ruby (~> 1.0)
zeitwerk (~> 2.6)
erubi (1.13.0)
globalid (1.2.1)
activesupport (>= 6.1)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
json (2.6.3)
language_server-protocol (3.17.0.3)
loofah (2.23.1)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.8.1)
mini_mime (>= 0.1.1)
net-imap
net-pop
net-smtp
marcel (1.0.4)
method_source (1.1.0)
mini_mime (1.1.5)
minitest (5.19.0)
net-imap (0.5.1)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.2)
timeout
net-smtp (0.5.0)
net-protocol
nio4r (2.7.4)
nokogiri (1.16.8-arm64-darwin)
racc (~> 1.4)
nokogiri (1.16.8-x86_64-linux)
racc (~> 1.4)
parallel (1.23.0)
Expand All @@ -71,6 +140,20 @@ GEM
rack (2.2.8)
rack-test (2.1.0)
rack (>= 1.3)
rails (7.0.7)
actioncable (= 7.0.7)
actionmailbox (= 7.0.7)
actionmailer (= 7.0.7)
actionpack (= 7.0.7)
actiontext (= 7.0.7)
actionview (= 7.0.7)
activejob (= 7.0.7)
activemodel (= 7.0.7)
activerecord (= 7.0.7)
activestorage (= 7.0.7)
activesupport (= 7.0.7)
bundler (>= 1.15.0)
railties (= 7.0.7)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
Expand Down Expand Up @@ -133,9 +216,13 @@ GEM
rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
thor (1.3.2)
timeout (0.4.2)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.4.2)
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
zeitwerk (2.6.11)

PLATFORMS
Expand All @@ -146,6 +233,7 @@ DEPENDENCIES
active_dry_deps!
activesupport
combustion
rails
rake (~> 13.0)
rspec (~> 3.0)
rubocop-gp!
Expand Down
12 changes: 8 additions & 4 deletions lib/active_dry_deps/container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
module ActiveDryDeps
class Container < Hash

def resolve(const_name)
unless key?(const_name)
self[const_name] = Object.const_get(const_name)
def resolve(container_key)
unless key?(container_key)
self[container_key] = Object.const_get(container_key)
end

self[const_name]
self[container_key]
end

def register(container_key, &block)
self[container_key.to_s] = block
end

end
Expand Down
138 changes: 87 additions & 51 deletions lib/active_dry_deps/dependency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,88 +3,124 @@
module ActiveDryDeps
class Dependency

VALID_METHOD_NAME = /^[a-zA-Z_0-9]+$/
VALID_CONST_NAME = /^[[:upper:]][a-zA-Z_0-9\:]*$/
METHODS_AS_KLASS = %w[perform_later call].freeze
LOWER = /[[:lower:]]/

attr_reader :receiver_method_name, :receiver_method, :const_name, :method_name
attr_reader :receiver_method_name, :resolve_key, :const_name, :method_name

def initialize(resolver, receiver_method_alias: nil)
if resolver.respond_to?(:call)
receiver_method_by_proc(resolver, receiver_method_alias: receiver_method_alias)
if LOWER.match?(resolver[0])
receiver_method_by_key(resolver, receiver_method_alias:)
else
receiver_method_by_const_name(resolver, receiver_method_alias: receiver_method_alias)
receiver_method_by_const_name(resolver, receiver_method_alias:)
end
end

def callable?
!@receiver_method.nil?
end

def receiver_method_string
if @method_name
<<~RUBY
# def create_order(*args, **options, &block)
# ::ActiveDryDeps::Deps::CONTAINER.resolve("OrderService::Create").call(*args, **options, &block)
# end
def #{@receiver_method_name}(*args, **options, &block)
::ActiveDryDeps::Deps::CONTAINER.resolve("#{@const_name}").#{@method_name}(*args, **options, &block)
end
RUBY
else
<<~RUBY
# def create_order_service
# ::ActiveDryDeps::Deps::CONTAINER.resolve("OrderService::Create")
# end
def #{@receiver_method_name}
::ActiveDryDeps::Deps::CONTAINER.resolve("#{@const_name}")
end
RUBY
if Rails.env.test?
def receiver_method_string
Kernel.sprintf(
METHOD_TEMPLATES.fetch(container: true, method_call: !@method_name.nil?),
receiver_method_name: @receiver_method_name,
container_key_or_const_name: @const_name || @resolve_key,
method_name: @method_name,
)
end
else
def receiver_method_string
Kernel.sprintf(
METHOD_TEMPLATES.fetch(container: !@resolve_key.nil?, method_call: !@method_name.nil?),
receiver_method_name: @receiver_method_name,
container_key_or_const_name: @const_name || @resolve_key,
method_name: @method_name,
)
end
end

private def receiver_method_by_proc(resolver, receiver_method_alias: nil)
unless receiver_method_alias
raise MissingAlias, 'Alias is required while injecting with Proc'
end
VALID_METHOD_NAME = /^[[[:alnum:]]_]+$/

@receiver_method_name = receiver_method_alias
private def receiver_method_by_key(resolver, receiver_method_alias: nil)
receiver_method_name = receiver_method_alias || resolver

unless VALID_METHOD_NAME.match?(@receiver_method_name.to_s)
raise DependencyNameInvalid, "name +#{@receiver_method_name}+ is not a valid Ruby identifier"
unless VALID_METHOD_NAME.match?(receiver_method_name.to_s)
raise DependencyNameInvalid, "name +#{receiver_method_name}+ is not a valid Ruby identifier"
end

@receiver_method = resolver
@receiver_method_name = receiver_method_name.to_sym
@resolve_key = resolver
@method_name = :call
end

VALID_CONST_NAME = /^[[:upper:]][[[:alnum:]]:_]*$/
METHODS_AS_KLASS = %w[perform_later call].freeze

private def receiver_method_by_const_name(resolver, receiver_method_alias: nil)
@const_name, @method_name = resolver.to_s.split('.', 2)
const_name, method_name = resolver.to_s.split('.', 2)

unless VALID_CONST_NAME.match?(@const_name)
unless VALID_CONST_NAME.match?(const_name)
raise DependencyNameInvalid, "+#{resolver}+ must contains valid constant name"
end

if @method_name && !VALID_METHOD_NAME.match?(@method_name)
raise DependencyNameInvalid, "name +#{@method_name}+ is not a valid Ruby identifier"
if method_name && !VALID_METHOD_NAME.match?(method_name)
raise DependencyNameInvalid, "name +#{method_name}+ is not a valid Ruby identifier"
end

@receiver_method_name =
receiver_method_name =
if receiver_method_alias
receiver_method_alias
elsif @method_name && METHODS_AS_KLASS.exclude?(@method_name)
@method_name
elsif @const_name
@const_name.split('::').last
elsif method_name && METHODS_AS_KLASS.exclude?(method_name)
method_name
elsif const_name
const_name.split('::').last
else
resolver
end

unless VALID_METHOD_NAME.match?(@receiver_method_name.to_s)
raise DependencyNameInvalid, "name +#{@receiver_method_name}+ is not a valid Ruby identifier"
unless VALID_METHOD_NAME.match?(receiver_method_name.to_s)
raise DependencyNameInvalid, "name +#{receiver_method_name}+ is not a valid Ruby identifier"
end

@receiver_method_name = receiver_method_name.to_sym
@const_name = const_name
@method_name = method_name&.to_sym
end

METHOD_TEMPLATES = {
{ container: true, method_call: true } => <<~RUBY,
# def create_order(...)
# ::ActiveDryDeps::Deps::CONTAINER.resolve("OrderService::Create").call(...)
# end
def %{receiver_method_name}(...)
::ActiveDryDeps::Deps::CONTAINER.resolve("%{container_key_or_const_name}").%{method_name}(...)
end
RUBY
{ container: true, method_call: false } => <<~RUBY,
# def create_order
# ::ActiveDryDeps::Deps::CONTAINER.resolve("OrderService::Create")
# end
def %{receiver_method_name}
::ActiveDryDeps::Deps::CONTAINER.resolve("%{container_key_or_const_name}")
end
RUBY
{ container: false, method_call: true } => <<~RUBY,
# def create_order(...)
# OrderService::Create.call(...)
# end
def %{receiver_method_name}(...)
%{container_key_or_const_name}.%{method_name}(...)
end
RUBY
{ container: false, method_call: false } => <<~RUBY,
# def create_order
# OrderService::Create
# end
def %{receiver_method_name}
%{container_key_or_const_name}
end
RUBY
}

end
end
23 changes: 9 additions & 14 deletions lib/active_dry_deps/deps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,25 @@ module Deps
# include Deps['Lib::Routes.admin'] use as `admin`
# include Deps['Lib::Routes'] use as `Routes()`
# include Deps['OrderService::Recalculate.call'] use as `Recalculate()`
# include Deps[send_email: -> { 'email-sent' }] use as `send_email`
def [](*keys, **aliases)
m = Module.new

dependencies = []
dependencies += keys.map { |resolver| Dependency.new(resolver) }
dependencies += aliases.map { |alias_method, resolver| Dependency.new(resolver, receiver_method_alias: alias_method) }
receiver_methods = +''

call_dependencies, constant_dependencies = dependencies.partition(&:callable?)

m.module_eval(constant_dependencies.map(&:receiver_method_string).join("\n"))
keys.each do |resolver|
receiver_methods << Dependency.new(resolver).receiver_method_string << "\n"
end

call_dependencies.each do |dependency|
m.define_method(dependency.receiver_method_name, &dependency.receiver_method)
aliases.each do |alias_method, resolver|
receiver_methods << Dependency.new(resolver, receiver_method_alias: alias_method).receiver_method_string << "\n"
end

m.module_eval(receiver_methods)
m
end

# TODO: необходимость сомнительна
def resolve(resolver)
dependency = Dependency.new(resolver)
m = Module.new { module_function module_eval(dependency.receiver_method_string) }
m.public_send(dependency.receiver_method_name)
def register(...)
CONTAINER.register(...)
end

end
Expand Down
Loading

0 comments on commit 94ea2e1

Please sign in to comment.