laters 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6ad4e042b43cac1765ea61b494fc1ec86ebe4fcfb68c888be4e2a7fdded4985
4
- data.tar.gz: 86b3ffe9dab1b85dc2ca73dac4417d3a8b71d25aadda551927e32d4389784a64
3
+ metadata.gz: 85e450085da66e2a32ab240a767416ee4f4c7cb04d891a4e6d4b1ec8c2febe15
4
+ data.tar.gz: 7ffc6eae552ccab18d82b24e736e4c1969fcb6aa51425c82489cea16e5cd0f79
5
5
  SHA512:
6
- metadata.gz: ce904311b5114eef4e8c7b4d2569a49c46ca17e184a570ae3829eda0a7f508b850fba966307d8de583eecb253e683a3280aee9ebfe793b90ee67d9d58085199e
7
- data.tar.gz: 2c8d9566b52771b4494bc01736f5db1e677d68a5ff36798534d18440ee0e9245c5bd8380ac6b8dcb73916523382b7de4c66e7e7e311384e235308b808d5a0073
6
+ metadata.gz: 4930a10d29d4704bc1848100d84d035f38d560f8c9ef5985d7e3d21d9c77c3541750f845762ec311be29cafa17e294ede912c230802fb4dc5ccd3428c97913fb
7
+ data.tar.gz: 4cf150e51867955a1941a42af2df24444962d934f7fe551b8fb4380d12bd12acd3466b0c6ebf0afc51635ed3ead2bf7e238693d46c8b025225759fb8abc724ae
data/CHANGELOG.md CHANGED
@@ -1,3 +1,35 @@
1
+ # 0.3.0 (18-3-2025)
2
+
3
+ ## New Features
4
+ - Added scheduled job support via `wait`, `wait_until`, and `priority` options
5
+ - Support ActiveJob scheduling options with the same interface as `perform_later`
6
+
7
+ ## Bug Fixes and Improvements
8
+ - Fixed compatibility issues with newer Ruby and Rails versions
9
+ - Fixed DidYouMean deprecation warnings by upgrading Thor dependency
10
+ - Fixed Logger class compatibility with ActiveSupport
11
+ - Improved test suite with additional edge cases
12
+
13
+ # 0.2.0 (15-3-2025)
14
+
15
+ ## Major Enhancements
16
+ - Added official support for Rails 5.0 through 8.0
17
+ - Added support for Ruby 3.0 through 3.4
18
+ - Added comprehensive YARD documentation for all methods and classes
19
+
20
+ ## Improvements
21
+ - Enhanced callback support with comprehensive tests (`before_laters`, `after_laters`, `around_laters`)
22
+ - Added proper keyword arguments support throughout the codebase
23
+ - Fixed compatibility with ActiveJob in Rails 7+
24
+ - Updated Thor dependency to fix DidYouMean warnings
25
+ - Added test cases for all scheduling and parameter options
26
+ - Added time-related testing helpers to improve test reliability
27
+
28
+ # 0.1.2 (26-8-2020)
29
+
30
+ - Add build badge to readme
31
+ - Remove Zeitwerk
32
+
1
33
  # 0.1.1 (23-8-2020)
2
34
 
3
35
  - Rename gem to `laters`
data/CLAUDE.md ADDED
@@ -0,0 +1,30 @@
1
+ # CLAUDE.md - Laters Ruby Gem
2
+
3
+ ## Build/Test Commands
4
+ - Run all tests: `bundle exec rake spec`
5
+ - Run single test: `bundle exec rspec spec/path_to_spec.rb:line_number`
6
+ - Run RuboCop lint: `bundle exec rubocop`
7
+ - Install dependencies: `bundle install`
8
+ - Test with specific Rails version: `RAILS_VERSION=7.0 bundle install && bundle exec rake`
9
+
10
+ ## Code Style Guidelines
11
+ - Follow standard Ruby style conventions
12
+ - Use RuboCop 1.25+ for linting
13
+ - RSpec tests use expect syntax (not should syntax)
14
+ - Use 2 space indentation
15
+ - Private methods are grouped with `private` keyword
16
+ - Callbacks are defined after regular methods
17
+ - Class methods are defined before instance methods
18
+ - ActiveRecord models follow Rails naming conventions
19
+ - Error handling uses custom `Laters::Error` class
20
+ - Use keyword arguments in method signatures for Rails 7+ compatibility
21
+ - Documentation in README for public API features
22
+
23
+ ## Project Structure
24
+ The gem follows standard Rails gem structure with:
25
+ - `lib/laters.rb` as main entry point
26
+ - ActiveRecord models include `Laters::Concern`
27
+ - Test suite uses RSpec with Combustion for Rails engine testing
28
+ - Queue configuration via `run_in_queue` class method
29
+ - Callbacks: `before_laters`, `after_laters`, `around_laters`
30
+ - Compatible with Rails 5.0 through 8 and Ruby 3.0 through 3.4
data/README.md CHANGED
@@ -1,8 +1,12 @@
1
- # Laters
1
+ # Laters 👋
2
+ [![Gem Version](https://badge.fury.io/rb/laters.svg)](https://badge.fury.io/rb/laters)
2
3
 
3
- Run any instance_method of ActiveRecord models via a job by adding `_later` to it. Laters 👋, means See you later in
4
+ Run any instance_method of ActiveRecord models via a job by adding `_later` to it. Laters, means See you later in
4
5
  Dutch 🇳🇱
5
6
 
7
+ Compatible with Rails 5.0 through 8 and Ruby 3.0 through 3.4.
8
+
9
+
6
10
  ## Installation
7
11
 
8
12
  Add to your Gemfile
@@ -23,7 +27,7 @@ class User < ApplicationRecord
23
27
  include Laters::Concern
24
28
 
25
29
  after_create_commit :notify_user_later
26
- after_commit :refresh_cache_later
30
+ after_commit :generate_ai_summary_later
27
31
 
28
32
  private
29
33
 
@@ -32,8 +36,18 @@ class User < ApplicationRecord
32
36
  Sms.send(to: user.phone, message: 'Hey!')
33
37
  end
34
38
 
35
- def refresh_cache!
36
- # Expensive calculation
39
+ def generate_ai_summary
40
+ # Call Claude API to generate a summary asynchronously
41
+ prompt = "Summarize this user profile: #{name}, #{bio}"
42
+
43
+ response = AnthropicClient.complete(
44
+ model: "claude-3-7-sonnet",
45
+ prompt: prompt,
46
+ max_tokens: 150
47
+ )
48
+
49
+ # Store the AI-generated summary
50
+ update(ai_summary: response.completion)
37
51
  end
38
52
  end
39
53
  ```
@@ -48,7 +62,27 @@ class User < ApplicationRecord
48
62
  end
49
63
  ```
50
64
 
51
- If you need callbacks, they are provided as standart model callbacks:
65
+ ### Scheduling Options
66
+
67
+ You can use ActiveJob's scheduling options when calling methods with `_later`:
68
+
69
+ ```rb
70
+ # Run 5 minutes from now
71
+ user.send_welcome_email_later(wait: 5.minutes)
72
+
73
+ # Run at a specific time
74
+ user.send_welcome_email_later(wait_until: 1.day.from_now)
75
+
76
+ # Set a priority (if supported by your queue adapter)
77
+ user.send_welcome_email_later(priority: 10)
78
+
79
+ # With method arguments
80
+ user.send_email_later('Welcome!', cc: admin@example.com, wait: 10.minutes)
81
+ ```
82
+
83
+ ### Callbacks
84
+
85
+ If you need callbacks, they are provided as standard model callbacks:
52
86
 
53
87
  ```rb
54
88
  class User < ApplicationRecord
data/Rakefile CHANGED
@@ -1,6 +1,19 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
+ # Silence the DidYouMean deprecation warnings
5
+ if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error) && defined?(DidYouMean::SPELL_CHECKERS)
6
+ # Monkey patch DidYouMean to suppress deprecation warnings
7
+ DidYouMean::SPELL_CHECKERS.singleton_class.prepend(Module.new do
8
+ def merge!(error_name, spell_checker)
9
+ DidYouMean.correct_error(error_name, spell_checker)
10
+ end
11
+ end)
12
+ end
13
+
14
+ # Silence URI redefinition warnings in Ruby 3.2+
15
+ $VERBOSE = nil if RUBY_VERSION >= '3.2.0'
16
+
4
17
  RSpec::Core::RakeTask.new(:spec)
5
18
 
6
19
  task :default => :spec
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Laters
4
+ # Main concern that provides asynchronous execution of ActiveRecord model methods
5
+ #
6
+ # This module adds the ability to defer execution of an instance method
7
+ # to a background job by calling a method with the `_later` suffix.
8
+ #
9
+ # @example
10
+ # class User < ActiveRecord::Base
11
+ # include Laters::Concern
12
+ #
13
+ # def send_welcome_email
14
+ # # Some long running task
15
+ # end
16
+ # end
17
+ #
18
+ # # Basic usage (executes in background)
19
+ # user.send_welcome_email_later
20
+ #
21
+ # # With scheduling options
22
+ # user.send_welcome_email_later(wait: 5.minutes)
23
+ # user.send_welcome_email_later(wait_until: 1.day.from_now)
24
+ #
25
+ module Concern
26
+ extend ActiveSupport::Concern
27
+ include ActiveModel::Callbacks
28
+
29
+ # Define laters callbacks when concern is included
30
+ included { define_model_callbacks :laters }
31
+
32
+ class_methods do
33
+ # @return [Symbol, nil] The queue name to use for background jobs
34
+ attr_reader :job_queue
35
+
36
+ private
37
+
38
+ # Configure the queue to use for background jobs
39
+ #
40
+ # @param [Symbol] queue The queue name to use for jobs
41
+ # @return [Symbol] The configured queue name
42
+ # @example
43
+ # class Comment < ActiveRecord::Base
44
+ # include Laters::Concern
45
+ # run_in_queue :low
46
+ # end
47
+ def run_in_queue(queue)
48
+ @job_queue = queue
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ # Handles method calls with _later suffix by enqueueing background jobs
55
+ #
56
+ # @param [Symbol] method The method being called
57
+ # @param [Array] args Arguments to pass to the method
58
+ # @param [Hash] kwargs Keyword arguments to pass to the method
59
+ # Special kwargs for job scheduling are extracted:
60
+ # @option kwargs [Integer] :wait Time in seconds to wait before executing
61
+ # @option kwargs [Time] :wait_until Specific time to execute the job
62
+ # @option kwargs [Integer] :priority Priority for the job
63
+ # @param [Proc] block Block to pass to the method (unused)
64
+ # @return [ActiveJob::Base] The enqueued job
65
+ # @raise [NoMethodError] If the method doesn't exist
66
+ def method_missing(method, *args, **kwargs, &block)
67
+ if (method_to_call = deferrable_method_name(method))
68
+ # Extract ActiveJob options if they exist in kwargs
69
+ job_options = { queue: self.class.job_queue || :default }
70
+
71
+ # Move scheduling options from kwargs to job_options
72
+ job_options[:wait] = kwargs.delete(:wait) if kwargs.key?(:wait)
73
+ job_options[:wait_until] = kwargs.delete(:wait_until) if kwargs.key?(:wait_until)
74
+ job_options[:priority] = kwargs.delete(:priority) if kwargs.key?(:priority)
75
+
76
+ # Set all options at once
77
+ InstanceMethodJob
78
+ .set(job_options)
79
+ .perform_later(self, method_to_call, *args, **kwargs)
80
+ else
81
+ super
82
+ end
83
+ end
84
+
85
+ # Determines if the object responds to a method with _later suffix
86
+ #
87
+ # @param [Symbol] method_name The method name to check
88
+ # @param [Boolean] include_private Whether to include private methods
89
+ # @return [Boolean] True if the method can be handled
90
+ def respond_to_missing?(method_name, include_private = false)
91
+ method_name.to_s.ends_with?('_later') || super
92
+ end
93
+
94
+ # Extracts the actual method name from a method with _later suffix
95
+ #
96
+ # @param [Symbol] deferring_method Name of the deferring method that was called
97
+ # @return [String, nil] The actual method name to call or nil if not applicable
98
+ # @example
99
+ # deferrable_method_name(:send_email_later) # => "send_email"
100
+ # deferrable_method_name(:save_later) # => "save!" (if save! exists)
101
+ def deferrable_method_name(deferring_method)
102
+ return unless (method = deferring_method.to_s).ends_with? '_later'
103
+
104
+ method.chomp! '_later'
105
+ if respond_to? "#{method}!", true
106
+ "#{method}!"
107
+ elsif respond_to? method, true
108
+ method
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Laters
4
+ # Job class that executes deferred instance methods with callbacks
5
+ #
6
+ # This job is responsible for executing a method on an ActiveRecord object
7
+ # in the background. It runs all registered callbacks around the method execution.
8
+ #
9
+ # @example
10
+ # # This job is automatically used by the Laters::Concern
11
+ # InstanceMethodJob.perform_later(user, "send_welcome_email", arg1, arg2)
12
+ #
13
+ class InstanceMethodJob < (Rails.application.config.respond_to?(:active_job) ?
14
+ Rails.application.config.active_job.base_job || ActiveJob::Base :
15
+ ActiveJob::Base)
16
+ # Executes the specified method on the given object
17
+ #
18
+ # @param [Object] object The object to call the method on
19
+ # @param [String] method_name The name of the method to call
20
+ # @param [Array] args Arguments to pass to the method
21
+ # @param [Hash] kwargs Keyword arguments to pass to the method
22
+ # @return [Object] The result of the method call
23
+ # @raise [Exception] Any exception raised by the method
24
+ def perform(object, method_name, *args, **kwargs)
25
+ if object.respond_to? :id
26
+ Rails.logger.info "Calling deferred #{method_name} on #{object.class} ##{object.id}"
27
+ else
28
+ Rails.logger.info "Calling deferred #{object.class}##{method_name}"
29
+ end
30
+
31
+ object.run_callbacks(:laters) { object.send(method_name, *args, **kwargs) }
32
+ end
33
+ end
34
+ end
@@ -1,3 +1,3 @@
1
1
  module Laters
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
data/lib/laters.rb CHANGED
@@ -1,14 +1,11 @@
1
1
  require 'laters/version'
2
+
3
+ require 'active_support'
2
4
  require 'active_model'
3
5
  require 'active_job'
4
- require 'zeitwerk'
5
- loader = Zeitwerk::Loader.for_gem
6
- loader.push_dir('app/models/concerns')
7
- loader.push_dir('app/jobs')
8
- loader.setup
6
+ require 'laters/concern'
7
+ require 'laters/instance_method_job'
9
8
 
10
9
  module Laters
11
10
  class Error < StandardError; end
12
11
  end
13
-
14
- loader.eager_load
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: laters
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
  - Kieran Klaassen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-08-23 00:00:00.000000000 Z
12
+ date: 2025-03-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -17,28 +17,34 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '4.2'
20
+ version: '5.0'
21
+ - - "<"
22
+ - !ruby/object:Gem::Version
23
+ version: '9'
21
24
  type: :runtime
22
25
  prerelease: false
23
26
  version_requirements: !ruby/object:Gem::Requirement
24
27
  requirements:
25
28
  - - ">="
26
29
  - !ruby/object:Gem::Version
27
- version: '4.2'
30
+ version: '5.0'
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '9'
28
34
  - !ruby/object:Gem::Dependency
29
- name: zeitwerk
35
+ name: thor
30
36
  requirement: !ruby/object:Gem::Requirement
31
37
  requirements:
32
38
  - - ">="
33
39
  - !ruby/object:Gem::Version
34
- version: '0'
40
+ version: 1.2.0
35
41
  type: :runtime
36
42
  prerelease: false
37
43
  version_requirements: !ruby/object:Gem::Requirement
38
44
  requirements:
39
45
  - - ">="
40
46
  - !ruby/object:Gem::Version
41
- version: '0'
47
+ version: 1.2.0
42
48
  description: Deferrable empowers a class to run every single defined method wrapped
43
49
  in an ActiveJob of any class that includes it
44
50
  email:
@@ -48,13 +54,14 @@ extensions: []
48
54
  extra_rdoc_files: []
49
55
  files:
50
56
  - CHANGELOG.md
57
+ - CLAUDE.md
51
58
  - CODE_OF_CONDUCT.md
52
59
  - LICENSE.txt
53
60
  - README.md
54
61
  - Rakefile
55
- - app/jobs/laters/instance_method_job.rb
56
- - app/models/concerns/laters/concern.rb
57
62
  - lib/laters.rb
63
+ - lib/laters/concern.rb
64
+ - lib/laters/instance_method_job.rb
58
65
  - lib/laters/version.rb
59
66
  homepage: https://github.com/kieranklaassen/laters
60
67
  licenses:
@@ -71,14 +78,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
71
78
  requirements:
72
79
  - - ">="
73
80
  - !ruby/object:Gem::Version
74
- version: 2.3.0
81
+ version: 3.0.0
82
+ - - "<"
83
+ - !ruby/object:Gem::Version
84
+ version: '3.5'
75
85
  required_rubygems_version: !ruby/object:Gem::Requirement
76
86
  requirements:
77
87
  - - ">="
78
88
  - !ruby/object:Gem::Version
79
89
  version: '0'
80
90
  requirements: []
81
- rubygems_version: 3.1.2
91
+ rubygems_version: 3.4.10
82
92
  signing_key:
83
93
  specification_version: 4
84
94
  summary: Run any instance_method in ActiveRecord models via a job by adding `_later`
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Laters
4
- class InstanceMethodJob < ActiveJob::Base
5
- def perform(object, method_name, *args)
6
- if object.respond_to? :id
7
- Rails.logger.info "Calling deferred #{method_name} on #{object.class} ##{object.id}"
8
- else
9
- Rails.logger.info "Calling deferred #{object.class}##{method_name}"
10
- end
11
-
12
- object.run_callbacks(:laters) { object.send(method_name, *args) }
13
- end
14
- end
15
- end
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Laters
4
- module Concern
5
- extend ActiveSupport::Concern
6
- include ActiveModel::Callbacks
7
-
8
- included { define_model_callbacks :laters }
9
-
10
- class_methods do
11
- attr_reader :job_queue
12
-
13
- private
14
-
15
- def run_in_queue(queue)
16
- @job_queue = queue
17
- end
18
- end
19
-
20
- private
21
-
22
- def method_missing(method, *args, &block)
23
- if (method_to_call = deferrable_method_name(method))
24
- InstanceMethodJob
25
- .set(queue: self.class.job_queue || :default)
26
- .perform_later(self, method_to_call, *args)
27
- else
28
- super
29
- end
30
- end
31
-
32
- def respond_to_missing?(method_name, include_private = false)
33
- method_name.to_s.ends_with?('_later') || super
34
- end
35
-
36
- # @param [Symbol] deferring_method Name of the deferring method that was called
37
- def deferrable_method_name(deferring_method)
38
- return unless (method = deferring_method.to_s).ends_with? '_later'
39
-
40
- method.chomp! '_later'
41
- if respond_to? "#{method}!", true
42
- "#{method}!"
43
- elsif respond_to? method, true
44
- method
45
- end
46
- end
47
- end
48
- end