odsl 0.1.0 → 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,19 +1,70 @@
1
+ # lib/odsl/methods.rb
1
2
  module Odsl
2
3
  module Methods
4
+
5
+ def use(object)
6
+ @delegated = instance_variable_get("@#{object}")
7
+ self
8
+ end
9
+
3
10
  def call(methodo)
4
-
5
- @last_results = send(methodo, *@with_attributes.map { |attribute| instance_variable_get("@#{attribute}") })
6
- @last_results = [@last_results] unless @last_results.is_a? Array
7
-
11
+ run_method(methodo)
12
+ restore_delegate
8
13
  self
9
14
  end
10
-
15
+
11
16
  def to(*variables)
12
17
  variables.each_with_index do |variable, index|
13
- 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
14
30
  end
15
-
16
31
  self
17
32
  end
33
+
34
+ private
35
+
36
+ def run_method(methodo)
37
+ @last_results = @delegated.send(methodo, *with_attributes_resolver)
38
+ @last_results = [@last_results] unless @last_results.is_a?(Array)
39
+ end
40
+
41
+ def restore_delegate
42
+ @delegated = self
43
+ end
44
+
45
+ def with_attributes_resolver
46
+ return [] if @with_attributes.nil?
47
+ @with_attributes.map { |attribute| instance_variable_get("@#{attribute}") }
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
18
69
  end
19
- 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,9 +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
71
+
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
4
117
 
5
- instance_eval(method.to_s)
6
- instance_variable_get "@#{@finish_with}"
118
+ def _finish_result
119
+ if @finish_with.is_a?(Array)
120
+ @finish_with.map { |variable| instance_variable_get("@#{variable}") }
121
+ else
122
+ instance_variable_get("@#{@finish_with}")
123
+ end
7
124
  end
8
125
  end
9
- 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.0"
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
@@ -1,44 +1,49 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path(
4
- File.join('..', 'lib', 'odsl', 'version'),
5
- __FILE__
6
- )
7
-
8
- Gem::Specification.new do |spec|
9
- spec.name = "odsl"
10
- spec.version = Odsl::VERSION
11
- spec.authors = ["alejandro gurovich"]
12
- spec.email = ["alejandro.gurovich@gmail.com"]
13
-
14
- spec.summary = "DSL Approach to OOP and functional programming."
15
- spec.description = "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
16
- - Inmutability
17
- - Dry off
18
- - Decouple aspects from code ( background, paralelization )."
19
- spec.homepage = "https://github.com/aleguro/ODSL."
20
- spec.license = "MIT"
21
- spec.required_ruby_version = ">= 2.6.0"
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"
26
-
27
- # Specify which files should be added to the gem when it is released.
28
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
29
- spec.files = Dir.chdir(__dir__) do
30
- `git ls-files -z`.split("\x0").reject do |f|
31
- (File.expand_path(f) == __FILE__) ||
32
- f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
33
- end
34
- end
35
- spec.bindir = "exe"
36
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
37
- spec.require_paths = ["lib"]
38
-
39
- spec.add_development_dependency "minitest", ">= 5.8"
40
- spec.add_development_dependency "minitest-reporters", ">= 1.1"
41
-
42
- # For more information and examples about making a new gem, check out our
43
- # guide at: https://bundler.io/guides/creating_gem.html
44
- end
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path(
4
+ File.join('..', 'lib', 'odsl', 'version'),
5
+ __FILE__
6
+ )
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = "odsl"
10
+ spec.version = Odsl::VERSION
11
+ spec.authors = ["alejandro gurovich"]
12
+ spec.email = ["alejandro.gurovich@gmail.com"]
13
+
14
+ spec.summary = "DSL Approach to OOP and functional programming."
15
+ spec.description = "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
16
+ - Inmutability
17
+ - Dry off
18
+ - Decouple aspects from code ( background, paralelization )."
19
+ spec.homepage = "https://github.com/aleguro/ODSL"
20
+ spec.license = "MIT"
21
+ spec.required_ruby_version = ">= 3.2.0"
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"
26
+ spec.metadata["rubygems_mfa_required"] = "true"
27
+
28
+ # Specify which files should be added to the gem when it is released.
29
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
30
+ spec.files = Dir.chdir(__dir__) do
31
+ `git ls-files -z`.split("\x0").reject do |f|
32
+ (File.expand_path(f) == __FILE__) ||
33
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
34
+ end
35
+ end
36
+ spec.bindir = "exe"
37
+ spec.executables = [] #spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
38
+ spec.require_paths = ["lib"]
39
+
40
+ spec.add_development_dependency "minitest", ">= 5.8"
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"
46
+
47
+ # For more information and examples about making a new gem, check out our
48
+ # guide at: https://bundler.io/guides/creating_gem.html
49
+ end
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.0
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-23 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,26 +107,43 @@ 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
66
138
  - sig/odsl.rbs
67
- homepage: https://github.com/aleguro/ODSL.
139
+ homepage: https://github.com/aleguro/ODSL
68
140
  licenses:
69
141
  - MIT
70
142
  metadata:
71
- homepage_uri: https://github.com/aleguro/ODSL.
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