odsl 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/odsl/methods.rb CHANGED
@@ -1,25 +1,33 @@
1
+ # lib/odsl/methods.rb
1
2
  module Odsl
2
3
  module Methods
3
4
 
4
5
  def use(object)
5
6
  @delegated = instance_variable_get("@#{object}")
6
-
7
7
  self
8
8
  end
9
9
 
10
10
  def call(methodo)
11
-
12
11
  run_method(methodo)
13
12
  restore_delegate
14
-
15
13
  self
16
14
  end
17
-
15
+
18
16
  def to(*variables)
19
17
  variables.each_with_index do |variable, index|
20
- instance_variable_set("@#{variable}", @last_results[index])
18
+ value = @last_results[index]
19
+
20
+ if @_resuming_steps&.include?(variable.to_s)
21
+ stored = @_execution_context
22
+ .step_records
23
+ .find_by(name: variable.to_s)
24
+ &.deserialized_result
25
+ instance_variable_set("@#{variable}", stored)
26
+ else
27
+ instance_variable_set("@#{variable}", value)
28
+ _record_step(variable, value)
29
+ end
21
30
  end
22
-
23
31
  self
24
32
  end
25
33
 
@@ -27,7 +35,7 @@ module Odsl
27
35
 
28
36
  def run_method(methodo)
29
37
  @last_results = @delegated.send(methodo, *with_attributes_resolver)
30
- @last_results = [ @last_results ] unless @last_results.is_a? Array
38
+ @last_results = [@last_results] unless @last_results.is_a?(Array)
31
39
  end
32
40
 
33
41
  def restore_delegate
@@ -35,7 +43,28 @@ module Odsl
35
43
  end
36
44
 
37
45
  def with_attributes_resolver
46
+ return [] if @with_attributes.nil?
38
47
  @with_attributes.map { |attribute| instance_variable_get("@#{attribute}") }
39
48
  end
49
+
50
+ def _record_step(name, value)
51
+ return unless @_execution_context
52
+
53
+ @_step_index ||= 0
54
+ serializable = Odsl::Audit::Serializer.serializable?(value)
55
+
56
+ Odsl::Audit::StepRecord.create!(
57
+ execution_context: @_execution_context,
58
+ name: name.to_s,
59
+ step_index: @_step_index,
60
+ status: "completed",
61
+ result: serializable ? Odsl::Audit::Serializer.dump(value) : nil,
62
+ non_resumable: !serializable,
63
+ started_at: Time.now,
64
+ completed_at: Time.now
65
+ )
66
+
67
+ @_step_index += 1
68
+ end
40
69
  end
41
- end
70
+ end
@@ -0,0 +1,15 @@
1
+ require "rails/railtie"
2
+
3
+ module Odsl
4
+ class Railtie < Rails::Railtie
5
+ rake_tasks do
6
+ namespace :odsl do
7
+ desc "Remove expired audit records"
8
+ task cleanup: :environment do
9
+ count = Odsl.cleanup_expired!
10
+ puts "Removed #{count} expired execution context(s)"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/odsl/runners.rb CHANGED
@@ -1,15 +1,126 @@
1
+ # lib/odsl/runners.rb
2
+ require "json"
3
+
1
4
  module Odsl
2
5
  module Runners
3
- def run(method)
6
+ def run(method, key: nil)
7
+ if self.class.inmutable_mode
8
+ _run_immutable(method, key: key)
9
+ else
10
+ _run_method(method, key: key)
11
+ end
12
+ end
13
+
14
+ def run_in_background(method, key: nil)
15
+ _background_adapter.enqueue(
16
+ service_class: self.class.name,
17
+ method_name: method.to_s,
18
+ kwargs: @kwargs,
19
+ key: key
20
+ )
21
+ end
22
+
23
+ def run_in_parallel(*methods_and_opts)
24
+ key = methods_and_opts.last.is_a?(Hash) ? methods_and_opts.pop[:key] : nil
25
+ handles = methods_and_opts.map { |m| run_in_background(m, key: key) }
26
+ handles.map { |h| h.respond_to?(:value) ? h.value : h }
27
+ end
28
+
29
+ private
30
+
31
+ def _background_adapter
32
+ case Odsl.config.background_adapter
33
+ when :thread then Odsl::Adapters::Thread
34
+ when :active_job then Odsl::Adapters::ActiveJob
35
+ else raise Odsl::Error, "Unknown background adapter: #{Odsl.config.background_adapter.inspect}"
36
+ end
37
+ end
38
+
39
+ def _run_method(method, key: nil)
40
+ ctx_result = _setup_context(method, key)
41
+ return nil if ctx_result == :already_completed
42
+
43
+ @_execution_context = ctx_result
44
+ @_current_lock = nil
45
+ @_step_index = 0
46
+ @_current_method = method.to_s
47
+
48
+ begin
49
+ instance_eval(method.to_s)
50
+ @_execution_context&.update!(status: "completed")
51
+ _finish_result
52
+ rescue Odsl::LockError
53
+ raise
54
+ rescue
55
+ @_execution_context&.update!(status: "failed")
56
+ raise
57
+ ensure
58
+ @_current_lock&.unlock rescue nil
59
+ @_current_lock = nil
60
+ end
61
+ end
62
+
63
+ def _run_immutable(method, key: nil)
64
+ copy = dup
65
+ copy.instance_variable_set(:@context, @context.dup)
66
+ copy.instance_variable_set(:@last_results, [])
67
+ copy.instance_variable_set(:@with_attributes, nil)
68
+ copy.instance_variable_set(:@finish_with, nil)
69
+ copy.send(:_run_method, method, key: key)
70
+ end
4
71
 
5
- instance_eval(method.to_s)
72
+ def _resume_method(method, key: nil)
73
+ @_current_lock = nil
74
+ @_step_index = @_execution_context.step_records.count
75
+ @_current_method = method.to_s
76
+
77
+ @_execution_context&.update!(status: "running")
78
+
79
+ begin
80
+ instance_eval(method.to_s)
81
+ @_execution_context&.update!(status: "completed")
82
+ _finish_result
83
+ rescue Odsl::LockError
84
+ raise
85
+ rescue
86
+ @_execution_context&.update!(status: "failed")
87
+ raise
88
+ ensure
89
+ @_current_lock&.unlock rescue nil
90
+ @_current_lock = nil
91
+ end
92
+ end
93
+
94
+ def _setup_context(method, key)
95
+ return nil unless key && defined?(Odsl::Audit::ExecutionContext)
96
+
97
+ ctx = Odsl::Audit::ExecutionContext.find_by(idempotency_key: key)
98
+
99
+ if ctx
100
+ return :already_completed if ctx.completed?
101
+ raise Odsl::AlreadyRunningError, "Job with key '#{key}' is already running" if ctx.running?
102
+ else
103
+ ttl = Odsl.config.audit_ttl
104
+ ctx = Odsl::Audit::ExecutionContext.create!(
105
+ idempotency_key: key,
106
+ service_class: self.class.name,
107
+ method_name: method.to_s,
108
+ kwargs: JSON.generate(@kwargs),
109
+ status: "pending",
110
+ expires_at: ttl ? Time.now + ttl : nil
111
+ )
112
+ end
113
+
114
+ ctx.update!(status: "running")
115
+ ctx
116
+ end
6
117
 
7
- if @finish_with.is_a? Array
8
-
118
+ def _finish_result
119
+ if @finish_with.is_a?(Array)
9
120
  @finish_with.map { |variable| instance_variable_get("@#{variable}") }
10
121
  else
11
- instance_variable_get "@#{@finish_with}"
122
+ instance_variable_get("@#{@finish_with}")
12
123
  end
13
124
  end
14
125
  end
15
- end
126
+ end
data/lib/odsl/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Odsl
4
- VERSION = "0.1.1"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/odsl.rb CHANGED
@@ -1,2 +1,12 @@
1
- require 'odsl/version'
2
- require 'odsl/dsl'
1
+ require "odsl/version"
2
+ require "odsl/errors"
3
+ require "odsl/configuration"
4
+ require "odsl/adapters/base"
5
+ require "odsl/adapters/thread"
6
+ require "odsl/adapters/active_job"
7
+ require "odsl/audit/serializer"
8
+ require "odsl/audit/execution_context"
9
+ require "odsl/audit/step_record"
10
+ require "odsl/locking/lock_handle"
11
+ require "odsl/dsl"
12
+ require "odsl/railtie" if defined?(Rails::Railtie)
data/odsl.gemspec CHANGED
@@ -18,11 +18,12 @@ Gem::Specification.new do |spec|
18
18
  - Decouple aspects from code ( background, paralelization )."
19
19
  spec.homepage = "https://github.com/aleguro/ODSL"
20
20
  spec.license = "MIT"
21
- spec.required_ruby_version = ">= 2.6.0"
21
+ spec.required_ruby_version = ">= 3.2.0"
22
22
 
23
- spec.metadata["homepage_uri"] = spec.homepage
24
- spec.metadata["source_code_uri"] = "https://github.com/aleguro/ODSL"
25
- spec.metadata["changelog_uri"] = "https://github.com/aleguro/ODSL/blob/master/CHANGELOG.md"
23
+ spec.metadata["homepage_uri"] = spec.homepage
24
+ spec.metadata["source_code_uri"] = "https://github.com/aleguro/ODSL"
25
+ spec.metadata["changelog_uri"] = "https://github.com/aleguro/ODSL/blob/master/CHANGELOG.md"
26
+ spec.metadata["rubygems_mfa_required"] = "true"
26
27
 
27
28
  # Specify which files should be added to the gem when it is released.
28
29
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -38,6 +39,10 @@ Gem::Specification.new do |spec|
38
39
 
39
40
  spec.add_development_dependency "minitest", ">= 5.8"
40
41
  spec.add_development_dependency "minitest-reporters", ">= 1.1"
42
+ spec.add_development_dependency "activerecord", ">= 7.0"
43
+ spec.add_development_dependency "sqlite3", ">= 1.7"
44
+ spec.add_development_dependency "redlock", ">= 1.3"
45
+ spec.add_development_dependency "railties", ">= 7.0"
41
46
 
42
47
  # For more information and examples about making a new gem, check out our
43
48
  # guide at: https://bundler.io/guides/creating_gem.html
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: odsl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - alejandro gurovich
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-25 00:00:00.000000000 Z
11
+ date: 2026-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -38,6 +38,62 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activerecord
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '7.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '7.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: redlock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '1.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '1.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: railties
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '7.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '7.0'
41
97
  description: |-
42
98
  This is a prototype oriented to use OOP and a functional programming approach by providing a DSL to decouple code logic from coding itself while enforcing good practices and providing - Design patterns
43
99
  - Inmutability
@@ -51,15 +107,31 @@ extra_rdoc_files: []
51
107
  files:
52
108
  - CHANGELOG.md
53
109
  - CODE_OF_CONDUCT.md
110
+ - CONTRIBUTING.md
54
111
  - LICENSE.txt
55
112
  - README.md
56
113
  - Rakefile
114
+ - docs/superpowers/plans/2026-10-09-background-adapters-audit-locking.md
115
+ - docs/superpowers/specs/2026-10-09-background-adapters-audit-design.md
116
+ - lib/generators/odsl/install_generator.rb
117
+ - lib/generators/odsl/templates/create_odsl_tables.rb.erb
118
+ - lib/generators/odsl/templates/odsl_initializer.rb.erb
57
119
  - lib/odsl.rb
120
+ - lib/odsl/adapters/active_job.rb
121
+ - lib/odsl/adapters/base.rb
122
+ - lib/odsl/adapters/odsl_job.rb
123
+ - lib/odsl/adapters/thread.rb
124
+ - lib/odsl/audit/execution_context.rb
125
+ - lib/odsl/audit/serializer.rb
126
+ - lib/odsl/audit/step_record.rb
58
127
  - lib/odsl/builder.rb
59
128
  - lib/odsl/configurable.rb
129
+ - lib/odsl/configuration.rb
60
130
  - lib/odsl/dsl.rb
61
- - lib/odsl/error.rb
131
+ - lib/odsl/errors.rb
132
+ - lib/odsl/locking/lock_handle.rb
62
133
  - lib/odsl/methods.rb
134
+ - lib/odsl/railtie.rb
63
135
  - lib/odsl/runners.rb
64
136
  - lib/odsl/version.rb
65
137
  - odsl.gemspec
@@ -71,6 +143,7 @@ metadata:
71
143
  homepage_uri: https://github.com/aleguro/ODSL
72
144
  source_code_uri: https://github.com/aleguro/ODSL
73
145
  changelog_uri: https://github.com/aleguro/ODSL/blob/master/CHANGELOG.md
146
+ rubygems_mfa_required: 'true'
74
147
  post_install_message:
75
148
  rdoc_options: []
76
149
  require_paths:
@@ -79,14 +152,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
79
152
  requirements:
80
153
  - - ">="
81
154
  - !ruby/object:Gem::Version
82
- version: 2.6.0
155
+ version: 3.2.0
83
156
  required_rubygems_version: !ruby/object:Gem::Requirement
84
157
  requirements:
85
158
  - - ">="
86
159
  - !ruby/object:Gem::Version
87
160
  version: '0'
88
161
  requirements: []
89
- rubygems_version: 3.3.7
162
+ rubygems_version: 3.5.22
90
163
  signing_key:
91
164
  specification_version: 4
92
165
  summary: DSL Approach to OOP and functional programming.
data/lib/odsl/error.rb DELETED
@@ -1,4 +0,0 @@
1
- module Odsl
2
- class Error < StandardError; end
3
- end
4
- end