ract 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48ebc9d97681afc2240e5001650e74d7250e76d20eb181955b402192377c8ca8
4
- data.tar.gz: 8c121a49a43a61beff3a8f5f068997585951fbb4acd6e4432b89e8cad571ba0d
3
+ metadata.gz: 3ecff096a9e2b43c0c289ba25728a9948b768feb5c9b58d1921d012b4f753b5d
4
+ data.tar.gz: 5e68a831315a19d56823f8ebace7b3e1b3da916d98fc5bd36cf3afb9e19b8d42
5
5
  SHA512:
6
- metadata.gz: 4b82bcf08d12aa28ffd6ac36f66572cb9b40a6a82e5f364d09eb37adbf0e79a7d7ac9f32a608dbd04dd62439487193ddd32ac3bca5d5a07f5d31de1306f8633b
7
- data.tar.gz: b6cf72683bd4a7bdf7b7fd7756af98726627c56321ca65938663d47bd8469d8982031d97b2786f347ad4894f27eb255a43ef6766e2870a71cb1f4c42738e4284
6
+ metadata.gz: c0fa3ffc6fbbc2d5e593847de4b1cff4624f8bece56c3e2ca327ff1a45bb0e21d786a395447045f1740edc8a166a959a34ccadefa071ffef10527e41cca828e6
7
+ data.tar.gz: 6bf63a41dc25fe9699417f675bdcbfc89164973f17e6562d98f5f2dbd8c9691755684839f2e37d1cba4f39d3379ed93b08b877af7c767708d5598dc9442bd095
data/.rubocop.yml ADDED
@@ -0,0 +1,84 @@
1
+ AllCops:
2
+ DisabledByDefault: false
3
+ NewCops: enable
4
+ TargetRubyVersion: 3.4
5
+ Exclude:
6
+ - 'vendor/**/*'
7
+ - 'bin/**/*'
8
+ - 'db/**/*'
9
+ - 'tmp/**/*'
10
+ - 'node_modules/**/*'
11
+
12
+ # Layout
13
+ Layout/LineLength:
14
+ Max: 120
15
+
16
+ Layout/EmptyLinesAroundModuleBody:
17
+ Enabled: true
18
+
19
+ Layout/EmptyLinesAroundClassBody:
20
+ Enabled: true
21
+
22
+ Layout/EmptyLinesAroundMethodBody:
23
+ Enabled: true
24
+
25
+ Layout/EmptyLinesAroundBlockBody:
26
+ Enabled: true
27
+
28
+ # Style
29
+ Style/Documentation:
30
+ Enabled: false
31
+
32
+ Style/DocumentationMethod:
33
+ Enabled: false
34
+
35
+ Style/StringLiterals:
36
+ EnforcedStyle: single_quotes
37
+ Enabled: true
38
+
39
+ Style/SymbolArray:
40
+ EnforcedStyle: brackets
41
+ Enabled: true
42
+
43
+ Style/WordArray:
44
+ EnforcedStyle: brackets
45
+ Enabled: true
46
+
47
+ Style/FrozenStringLiteralComment:
48
+ Enabled: true
49
+ SafeAutoCorrect: true
50
+
51
+ Style/TrailingCommaInArrayLiteral:
52
+ EnforcedStyleForMultiline: consistent_comma
53
+
54
+ Style/TrailingCommaInHashLiteral:
55
+ EnforcedStyleForMultiline: consistent_comma
56
+
57
+ # Metrics
58
+ Metrics/AbcSize:
59
+ Max: 20
60
+
61
+ Metrics/BlockLength:
62
+ Exclude:
63
+ - 'spec/**/*'
64
+ - '*.gemspec'
65
+
66
+ Metrics/ClassLength:
67
+ Max: 150
68
+
69
+ Metrics/MethodLength:
70
+ Max: 15
71
+
72
+ Metrics/CyclomaticComplexity:
73
+ Max: 8
74
+
75
+ # Naming
76
+ Naming/VariableName:
77
+ EnforcedStyle: snake_case
78
+
79
+ Naming/MethodName:
80
+ EnforcedStyle: snake_case
81
+
82
+ # Bundler
83
+ Bundler/OrderedGems:
84
+ Enabled: true
data/Gemfile CHANGED
@@ -14,5 +14,4 @@ group :test do
14
14
  gem 'rspec'
15
15
  gem 'simplecov', '~> 0.22.0', require: false
16
16
  gem 'simplecov-console', '~> 0.9.3', require: false
17
- gem 'standard', '~> 1.49'
18
17
  end
data/README.md CHANGED
@@ -1,10 +1,27 @@
1
- <h1 style="z-index: 999;">Ract</h1>
1
+ <div href="#" style="text-align: center; width: 50%; margin: 0 auto;">
2
+ <img src="./images/ract-no-bg.png" alt="Ract Logo" style="position: relative; margin-top: -50px; z-index: 1; display: flex; align-items: center; justify-content: center; text-align: center; mix-blend-mode: overlay;">
3
+ </div>
2
4
 
3
- <center>
4
- <img src="./images/ract-no-bg.png" alt="Ract Logo" width="65%" style="position: relative; margin-top: -50px; z-index: -1; mix-blend-mode: overlay;">
5
- </center>
5
+ <h1 style="z-index: 999; text-align: center;">Ract</h1>
6
6
 
7
- Ract is a lightweight Promise implementation for Ruby, providing a clean and intuitive way to handle asynchronous operations. It follows a similar pattern to JavaScript Promises, making it easy to write non-blocking code with proper error handling.
7
+ Ract is a lightweight Promise implementation for Ruby, simliar color promises in JavaScript providing a clean and intuitive way to handle asynchronous operations.
8
+
9
+ > [!NOTE]
10
+ >
11
+ > This gem don't use Ractor, we just Threads/Fibers to handle async operations
12
+ >
13
+ > Enjoy with us!
14
+
15
+
16
+ [![CI](https://github.com/thadeu/ract/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/thadeu/ract/actions/workflows/ci.yml)
17
+ [![Gem Version](https://badge.fury.io/rb/ract.svg)](https://badge.fury.io/rb/ract)
18
+
19
+ # Features
20
+
21
+ - Thread-safe
22
+ - Similar to JavaScript Promises
23
+ - Clean and intuitive API
24
+ - Easy to use
8
25
 
9
26
  ## Installation
10
27
 
@@ -46,7 +63,11 @@ Create a new promise with a block that will be executed asynchronously:
46
63
  # Create a promise that resolves with a value
47
64
  promise = Ract.new { 42 } # or just Ract { 42 }
48
65
 
49
- # Create a promise that might reject with an error
66
+ # Also use can you without .new
67
+ # IMO, That's is more readable
68
+ promise = Ract { 42 }
69
+
70
+ # Create a promise that might reject with an error inside de block
50
71
  promise = Ract.new do
51
72
  if success_condition
52
73
  "Success result"
@@ -56,6 +77,9 @@ promise = Ract.new do
56
77
  end
57
78
  ```
58
79
 
80
+ If you only create a IVar with `Ract`, it will be a pending promise, this promise will never resolve or reject itself,
81
+ you need to resolve it using `.then`, `.await`, `Ract#take`, `Ract#all`, `Ract#all_settled`
82
+
59
83
  ### Handling Promise Resolution
60
84
 
61
85
  Use `.then` to handle successful resolution:
@@ -120,10 +144,11 @@ Wait for multiple promises to complete:
120
144
  ```ruby
121
145
  # Wait for all promises to resolve (will raise an error if any promise rejects)
122
146
  promises = [Ract.new { task1 }, Ract.new { task2 }]
123
- combined = Ract.all(promises)
147
+
148
+ combined = Ract.take(promises)
124
149
 
125
150
  # Wait for all promises to resolve (will not raise errors, returns results with status)
126
- combined = Ract.all(promises, raise_on_error: false)
151
+ combined = Ract.take(promises, raise_on_error: false)
127
152
 
128
153
  # Get results when all are settled (resolved or rejected)
129
154
  results = Ract.all_settled(promises)
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true # :rdoc
2
+
3
+ class Ract
4
+ module ClassMethods
5
+ def resolve(value = nil, auto_execute: false)
6
+ new(auto_execute: true) { value }.await
7
+ end
8
+
9
+ def reject(reason = nil, auto_execute: false)
10
+ new(auto_execute: true) { raise Rejected, reason }.await
11
+ end
12
+
13
+ def all(array = [], raise_on_error: true, &block)
14
+ return [] if array.empty?
15
+
16
+ result = Settled.new(
17
+ array,
18
+ type: :all,
19
+ executor: executor,
20
+ raise_on_error: raise_on_error
21
+ ).run!
22
+
23
+ block.call(result.value) if block_given?
24
+
25
+ result.value
26
+ end
27
+ alias take all
28
+
29
+ def all_settled(array = [], &block)
30
+ return [] if array.empty?
31
+
32
+ result = Settled.new(
33
+ array,
34
+ type: :all_settled,
35
+ executor: executor,
36
+ raise_on_error: false
37
+ ).run!
38
+
39
+ block.call(result.value) if block_given?
40
+
41
+ result.value
42
+ end
43
+
44
+ def executor
45
+ case Ract.config.isolation_level
46
+ in :thread then Executor::IsolatedThread
47
+ else
48
+ raise ArgumentError, "Unknown executor: #{Ract.config.isolation_level}"
49
+ end
50
+ end
51
+ private :executor
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true # :rdoc:
2
+
3
+ class Ract
4
+ class Configuration
5
+ attr_accessor :isolation_level
6
+
7
+ def initialize
8
+ @isolation_level = :thread
9
+ end
10
+ end
11
+
12
+ class << self
13
+ def config
14
+ @config ||= Configuration.new
15
+ end
16
+
17
+ def configure
18
+ yield config
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true # :rdoc
2
+
3
+ class Ract
4
+ class Executor
5
+ class IsolatedAbstract
6
+ def initialize(...)
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def run(&block)
11
+ raise NotImplementedError
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true # :rdoc
2
+
3
+ class Ract
4
+ class Executor
5
+ class IsolatedThread < IsolatedAbstract
6
+ attr_reader :promises, :remaining
7
+
8
+ def initialize(promises)
9
+ @promises = promises.keep_if { |promise| promise.is_a?(Ract) }
10
+ @queue = Thread::Queue.new
11
+ @remaining = promises.size
12
+ end
13
+
14
+ def run(&block)
15
+ return if @promises.empty?
16
+
17
+ enqueue
18
+ dequeue(&block)
19
+ end
20
+
21
+ def enqueue
22
+ @promises.each_with_index do |promise, index|
23
+ Thread.new do
24
+ try_block!(promise)
25
+
26
+ promise.then do |value|
27
+ @queue << [:success, index, value]
28
+ end&.rescue do |reason|
29
+ @queue << [:error, index, reason]
30
+ end
31
+ rescue StandardError => e
32
+ @queue << [:error, index, e]
33
+ end
34
+ end
35
+ end
36
+
37
+ def dequeue(&block)
38
+ while @remaining.positive?
39
+ block.call(@queue.pop)
40
+ @remaining -= 1
41
+ end
42
+ end
43
+
44
+ def try_block!(arr)
45
+ return unless arr.respond_to?(:execute_block)
46
+ return unless arr.state == Ract::PENDING
47
+
48
+ arr.execute_block
49
+ end
50
+ end
51
+ end
52
+ end
data/lib/ract/ract.rb CHANGED
@@ -113,7 +113,7 @@ class Ract
113
113
  alias catch rescue
114
114
 
115
115
  class << self
116
- include SingleMethods
116
+ include ClassMethods
117
117
  end
118
118
 
119
119
  private
data/lib/ract/settled.rb CHANGED
@@ -4,17 +4,15 @@ class Ract
4
4
  class Settled
5
5
  attr_reader :type, :raise_on_error
6
6
 
7
- def initialize(promises, type: :all, raise_on_error: true)
7
+ def initialize(promises, executor: Executor::IsolatedThread, type: :all, raise_on_error: true)
8
8
  @results = Array.new(promises.size)
9
- @batch = Batch.new(promises)
9
+ @executor = executor.new(promises)
10
10
  @type = type
11
11
  @raise_on_error = raise_on_error
12
12
  end
13
13
 
14
14
  def run!
15
- return Result.new([]) if @batch.promises.empty?
16
-
17
- @batch.run! do |type, index, value|
15
+ @executor.run do |type, index, value|
18
16
  case type
19
17
  when :success
20
18
  @results[index] = success_row(value)
data/lib/ract/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true # :rdoc
2
2
 
3
3
  class Ract
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/ract.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'ract/version'
4
- require 'ract/batch'
4
+ require 'ract/executor/isolated_abstract'
5
+ require 'ract/executor/isolated_thread'
6
+ require 'ract/class_methods'
7
+ require 'ract/configuration'
5
8
  require 'ract/result'
6
9
  require 'ract/settled'
7
10
  require 'ract/async'
8
- require 'ract/single_methods'
9
11
  require 'ract/ract'
10
12
 
11
13
  # A lightweight Promise implementation for Ruby
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ract
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
  - Thadeu Esteves Jr
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-17 00:00:00.000000000 Z
10
+ date: 2025-04-18 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: A simple and lightweight gem to wrapper Threads(Promises) like JavaScript
13
13
  in Ruby, adding a color to Ruby Threads
@@ -19,6 +19,7 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - ".editorconfig"
21
21
  - ".rspec"
22
+ - ".rubocop.yml"
22
23
  - ".ruby-version"
23
24
  - CHANGELOG.md
24
25
  - Gemfile
@@ -30,19 +31,21 @@ files:
30
31
  - images/ract-no-bg.png
31
32
  - lib/ract.rb
32
33
  - lib/ract/async.rb
33
- - lib/ract/batch.rb
34
+ - lib/ract/class_methods.rb
35
+ - lib/ract/configuration.rb
36
+ - lib/ract/executor/isolated_abstract.rb
37
+ - lib/ract/executor/isolated_thread.rb
34
38
  - lib/ract/ract.rb
35
39
  - lib/ract/result.rb
36
40
  - lib/ract/settled.rb
37
- - lib/ract/single_methods.rb
38
41
  - lib/ract/version.rb
39
- homepage: https://github.com/thadeu/ract-rb
42
+ homepage: https://github.com/thadeu/ract
40
43
  licenses:
41
44
  - MIT
42
45
  metadata:
43
- homepage_uri: https://github.com/thadeu/ract-rb
44
- source_code_uri: https://github.com/thadeu/ract-rb
45
- changelog_uri: https://github.com/thadeu/ract-rb/blob/main/CHANGELOG.md
46
+ homepage_uri: https://github.com/thadeu/ract
47
+ source_code_uri: https://github.com/thadeu/ract
48
+ changelog_uri: https://github.com/thadeu/ract/blob/main/CHANGELOG.md
46
49
  rdoc_options: []
47
50
  require_paths:
48
51
  - lib
data/lib/ract/batch.rb DELETED
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true # :rdoc
2
-
3
- class Ract
4
- class Batch
5
- attr_reader :promises, :remaining
6
-
7
- def initialize(promises)
8
- @promises = promises.keep_if { |promise| promise.is_a?(Ract) }
9
- @queue = Thread::Queue.new
10
- @remaining = promises.size
11
- end
12
-
13
- def run!(&block)
14
- return if @promises.empty?
15
-
16
- create_threads
17
- process_results(&block)
18
- end
19
-
20
- def create_threads
21
- @promises.each_with_index do |promise, index|
22
- Thread.new do
23
- try_block!(promise)
24
-
25
- promise.then do |value|
26
- @queue << [:success, index, value]
27
- end&.rescue do |reason|
28
- @queue << [:error, index, reason]
29
- end
30
- rescue StandardError => e
31
- @queue << [:error, index, e]
32
- end
33
- end
34
- end
35
-
36
- def process_results(&block)
37
- while @remaining.positive?
38
- block.call(@queue.pop)
39
- @remaining -= 1
40
- end
41
- end
42
-
43
- def try_block!(promise)
44
- return unless promise.respond_to?(:execute_block)
45
- return unless promise.state == Ract::PENDING
46
-
47
- promise.execute_block
48
- end
49
- end
50
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true # :rdoc
2
-
3
- class Ract
4
- module SingleMethods
5
- def resolve(value = nil, auto_execute: false)
6
- new(auto_execute: true) { value }.await
7
- end
8
-
9
- def reject(reason = nil, auto_execute: false)
10
- new(auto_execute: true) { raise Rejected, reason }.await
11
- end
12
-
13
- def all(promises, raise_on_error: true, auto_execute: false, &block)
14
- return [] if promises.empty?
15
-
16
- result = Settled.new(promises, raise_on_error: raise_on_error).run!
17
-
18
- block.call(result.value) if block_given?
19
-
20
- result.value
21
- end
22
- alias take all
23
-
24
- def all_settled(promises, auto_execute: false, &block)
25
- return [] if promises.empty?
26
-
27
- result = Settled.new(promises, type: :all_settled).run!
28
-
29
- block.call(result.value) if block_given?
30
-
31
- result.value
32
- end
33
- end
34
- end