Skip to content

Commit fe86ff0

Browse files
committed
Make sidekiqable first version
1 parent d38b00d commit fe86ff0

5 files changed

Lines changed: 83 additions & 28 deletions

File tree

CLAUDE.md

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,18 @@ bin/setup # Install dependencies
4444
- Entry point module that classes extend to gain async capabilities
4545
- Defines three simple methods: `perform_async`, `perform_in(interval)`, and `perform_at(timestamp)`
4646
- Each method returns an `AsyncProxy` instance configured for the appropriate scheduling mode
47-
- No method wrapping or interception - just three straightforward class methods
47+
- Provides `sidekiqable_options(options)` method for per-class configuration
48+
- Options set per-class take precedence over global configuration
49+
- No method wrapping or interception - just straightforward class methods
4850

4951
**`Sidekiqable::AsyncProxy`** (lib/sidekiqable/async_proxy.rb)
5052
- Simple proxy returned by `perform_async`, `perform_in`, and `perform_at`
5153
- Uses `method_missing` to catch the actual method call (e.g., `.boo(1, 2)`)
52-
- Validates arguments are Sidekiq-serializable via `Sidekiq.dump_json`
54+
- Validates arguments are Sidekiq-serializable via `Sidekiq.dump_json` (can be disabled)
5355
- Raises error if blocks are passed (cannot be serialized)
5456
- Enqueues job to `Worker` with compact payload format: `["ClassName.method_name", *args]`
55-
- Applies global configuration options (queue, retry) before enqueuing
57+
- Applies configuration options before enqueuing using Sidekiq's `.set()` API
58+
- Configuration priority: per-class options > global options > Sidekiq defaults
5659

5760
**`Sidekiqable::Worker`** (lib/sidekiqable/worker.rb)
5861
- Standard Sidekiq worker that executes scheduled method calls
@@ -62,8 +65,10 @@ bin/setup # Install dependencies
6265

6366
**`Sidekiqable::Configuration`** (lib/sidekiqable/configuration.rb)
6467
- Global configuration object accessed via `Sidekiqable.configuration`
65-
- Supports `queue` and `retry` options applied to all jobs
66-
- Returns hash of Sidekiq options for worker configuration
68+
- Supports all standard Sidekiq worker options: `queue`, `retry`, `dead`, `backtrace`, `pool`, `tags`
69+
- Sidekiqable-specific option: `validate_arguments` (default: true)
70+
- Returns hash of Sidekiq options for worker configuration via `sidekiq_options`
71+
- Can be configured via `Sidekiqable.configure` or Rails' `config.sidekiqable`
6772

6873
**`Sidekiqable::Railtie`** (lib/sidekiqable/railtie.rb)
6974
- Rails integration that exposes `config.sidekiqable` for environment-specific configuration
@@ -84,18 +89,83 @@ bin/setup # Install dependencies
8489
2. Method executes immediately (no proxy involved)
8590
3. Returns result directly
8691

92+
### Configuration System
93+
94+
**Three levels of configuration (in order of precedence):**
95+
96+
1. **Per-class options** (highest priority) - Set via `sidekiqable_options` in the class
97+
```ruby
98+
class Foo
99+
extend Sidekiqable::AsyncableMethods
100+
sidekiqable_options queue: 'high', retry: 3
101+
end
102+
```
103+
104+
2. **Global configuration** - Set via `Sidekiqable.configure` or Rails `config.sidekiqable`
105+
```ruby
106+
Sidekiqable.configure do |config|
107+
config.queue = 'default'
108+
config.retry = 5
109+
config.validate_arguments = true
110+
end
111+
```
112+
113+
3. **Sidekiq defaults** (lowest priority) - Used when not configured
114+
115+
**Available options:**
116+
- Standard Sidekiq options: `queue`, `retry`, `dead`, `backtrace`, `pool`, `tags`
117+
- Sidekiqable-specific: `validate_arguments` (enables/disables argument serialization validation)
118+
119+
**How options are applied:**
120+
- `AsyncProxy` merges per-class and global options
121+
- Uses Sidekiq's `.set()` API to apply options: `Worker.set(options).perform_async(...)`
122+
- This happens at enqueue time, not worker definition time
123+
87124
### Implementation Notes
88125

89126
- No method wrapping or hooks - just simple `method_missing` on proxy objects
90127
- Compact payload format reduces serialization overhead
91128
- Clear separation between sync and async code paths
92129
- All complexity is isolated to the `AsyncProxy` class (~50 lines)
130+
- Configuration is applied dynamically at enqueue time, not on the Worker class itself
93131

94132
## Testing Notes
95133

96-
- Uses Minitest for testing
134+
- Uses Minitest for testing with Sidekiq test mode
97135
- Test helper location: test/test_helper.rb
98-
- Current test coverage is minimal (placeholder test exists in test/test_sidekiqable.rb)
136+
- Tests cover: version check, method presence, async enqueuing, delayed jobs, worker execution, block rejection, and sync calls
137+
- Run with `rake test` or `bundle exec rake test`
138+
139+
## Key Design Decisions
140+
141+
### Why the current API: `Foo.perform_async.bar(1, 2)`?
142+
143+
This syntax was chosen for clarity and simplicity:
144+
- **Clear intent**: Starting with `perform_async` makes async behavior explicit
145+
- **No method wrapping**: Avoids complex metaprogramming with `singleton_method_added` hooks
146+
- **Familiar**: Similar to Sidekiq's standard `Worker.perform_async` pattern
147+
- **Simple implementation**: Just 3 methods + proxy with `method_missing` (~60 total lines)
148+
149+
Alternative syntaxes considered:
150+
- `Foo.bar(1, 2).perform_async` - Requires wrapping ALL methods, adds overhead
151+
- `Foo.async.bar(1, 2)` - Similar to current, but less Sidekiq-like
152+
- `Foo.bar_async(1, 2)` - Requires suffix convention, less flexible
153+
154+
### Why compact payload format: `["Foo.bar", 1, 2]`?
155+
156+
- **Reduces serialization size**: One less array element per job
157+
- **Cleaner Sidekiq UI**: Job args look more natural
158+
- **No edge cases**: Method names can't contain dots in Ruby, so parsing is safe
159+
160+
Alternative considered:
161+
- `["Foo", "bar", 1, 2]` - More structured but slightly larger payload
162+
163+
### Why dynamic configuration via `.set()`?
164+
165+
Configuration is applied at enqueue time using `Worker.set(options)` rather than defining `sidekiq_options` on the Worker class:
166+
- **Flexibility**: Per-class options can override global config
167+
- **Single worker**: One `Worker` class handles all jobs, options vary per caller
168+
- **Standard Sidekiq pattern**: Uses `.set()` API that all Sidekiq users know
99169

100170
## Ruby Version
101171

lib/sidekiqable/async_proxy.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ def apply_config(worker_class)
4242
global_options = Sidekiqable.configuration.sidekiq_options
4343
class_options = @target_class.respond_to?(:sidekiqable_options) ? @target_class.sidekiqable_options : {}
4444

45-
# Merge global and per-class options (class options take precedence)
4645
options = global_options.merge(class_options)
4746

4847
options.empty? ? worker_class : worker_class.set(options)

lib/sidekiqable/configuration.rb

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ class Configuration
55
attr_accessor :queue, :retry, :dead, :backtrace, :pool, :tags, :validate_arguments
66

77
def initialize
8-
@queue = sidekiq_default("queue")
9-
@retry = sidekiq_default("retry")
10-
@dead = sidekiq_default("dead")
11-
@backtrace = sidekiq_default("backtrace")
8+
@queue = "default"
9+
@retry = true
10+
@dead = true
11+
@backtrace = false
1212
@pool = nil
1313
@tags = nil
1414
@validate_arguments = true
@@ -24,19 +24,5 @@ def sidekiq_options
2424
opts[:tags] = tags if tags
2525
end
2626
end
27-
28-
private
29-
30-
def sidekiq_default(option)
31-
Sidekiq::Worker::ClassMethods::DEFAULT_OPTIONS[option]
32-
rescue NameError
33-
# Fallback to Sidekiq's documented defaults if DEFAULT_OPTIONS is not available
34-
case option
35-
when "retry" then true
36-
when "queue" then "default"
37-
when "dead" then true
38-
when "backtrace" then false
39-
end
40-
end
4127
end
4228
end

lib/sidekiqable/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module Sidekiqable
4-
VERSION = "0.1.0"
4+
VERSION = "0.0.1"
55
end

lib/sidekiqable/worker.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
module Sidekiqable
44
class Worker
5-
include Sidekiq::Worker
5+
include Sidekiq::Job
66

77
def perform(callable, *args)
88
class_name, method_name = callable.split(".", 2)

0 commit comments

Comments
 (0)