postburner 1.0.0.pre.17 → 1.0.0.pre.18

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: 05d87a7d1949eb64ef1918b4bb0f0cdeb24796097c370f90576b3a158d0be5a7
4
- data.tar.gz: 857304a0b925574ab66d6ad215e6cbe3bdba8c109ecb9a34e96e8ab0ca40aac3
3
+ metadata.gz: 4af263bde4adb664e58df7f7045152e0fee7c29c953227aeb962b385b68de21d
4
+ data.tar.gz: 976c3a47d49bf83c26719e7b8adf17d05c827c0cd982855c5258307d703c4acf
5
5
  SHA512:
6
- metadata.gz: af92d06c7d796c0db2bb766986bc9b27ad46e9958103e11871538e3224b43a054d80fb50b7bb0b5f8d93de1a11331e10b10d26058c219047e15530f6a36a1b9e
7
- data.tar.gz: 351f86f49998e5350b7b705a4f77f68a91c29340995dc5531b964bbdb40b6bb55f3c4202459cdc7453429c7e3b7ceaa4ca934628fc8503305f9630eb9c287142
6
+ metadata.gz: 30e9379f4c7e05d18659f9864fb58c9e8a0f564575d323a9d3865b352ba3e0d08975bc9a3859d8044602f5c657c16bdd7dd32098944d07b6ad8007124f868a26
7
+ data.tar.gz: 1c0eb570bf338995bfa385a95c6d8ded11a937fb6155481b199e6a94f5992104d6ebc9524601056397f6251c89841d9148d7d45434e583d2e8f91e1e8f14aa66
data/README.md CHANGED
@@ -302,6 +302,7 @@ end
302
302
  Jobs without `Postburner::Beanstalkd` use defaults from `config/postburner.yml`:
303
303
  - `default_priority: 65536`
304
304
  - `default_ttr: 300`
305
+ - `default_queue: default` (if not specified, defaults to `'default'`)
305
306
 
306
307
  #### Configuring Third-Party Jobs
307
308
 
@@ -1278,6 +1279,8 @@ production: # <- environment config
1278
1279
  # config/postburner.yml
1279
1280
  default: &default
1280
1281
  beanstalk_url: <%= ENV['BEANSTALK_URL'] || 'beanstalk://localhost:11300' %>
1282
+ # default_queue: default # Queue for jobs (defaults to 'default')
1283
+ default_mailer_queue: mailers # Queue for Postburner::Mailer (defaults to default_queue)
1281
1284
 
1282
1285
  development: # <- environment config
1283
1286
  <<: *default
@@ -1781,7 +1784,9 @@ Set default priority in `config/postburner.yml`:
1781
1784
 
1782
1785
  ```yaml
1783
1786
  production:
1784
- default_priority: 65536 # Default without explicit priority set
1787
+ default_queue: default # Queue for jobs (defaults to 'default')
1788
+ default_mailer_queue: mailers # Queue for Postburner::Mailer (defaults to default_queue)
1789
+ default_priority: 65536 # Default without explicit priority set
1785
1790
  default_ttr: 300
1786
1791
  ```
1787
1792
 
@@ -202,10 +202,10 @@ module Postburner
202
202
  # job.queue_name # => 'urgent'
203
203
  #
204
204
  def queue_name
205
- @queue_name || self.class.queue || Postburner.configuration.default_queue_name
205
+ @queue_name || self.class.queue || Postburner.configuration.default_queue
206
206
  end
207
207
 
208
- # Returns the full tube name with environment prefix.
208
+ # Returns the full Beanstalkd tube name with environment prefix.
209
209
  #
210
210
  # Expands the queue name to include the environment prefix
211
211
  # (e.g., 'critical' becomes 'postburner.development.critical').
@@ -213,12 +213,18 @@ module Postburner
213
213
  # @return [String] Full tube name with environment prefix
214
214
  #
215
215
  # @example
216
- # job.expanded_tube_name # => 'postburner.development.critical'
216
+ # job.tube_name # => 'postburner.development.critical'
217
217
  #
218
- def expanded_tube_name
218
+ def tube_name
219
219
  Postburner.configuration.expand_tube_name(queue_name)
220
220
  end
221
221
 
222
+ # @!method expanded_tube_name
223
+ # Alias for {#tube_name}.
224
+ # @return [String] Full tube name with environment prefix
225
+ # @see #tube_name
226
+ alias_method :expanded_tube_name, :tube_name
227
+
222
228
  # Returns the priority for this job instance.
223
229
  #
224
230
  # Checks instance-level override first, then falls back to class-level configuration.
@@ -73,13 +73,13 @@ module Postburner
73
73
  #
74
74
  def stats
75
75
  # Get configured watched tubes (expanded with environment prefix)
76
- watched = Postburner.watched_tube_names.include?(self.expanded_tube_name)
76
+ watched = Postburner.watched_tube_names.include?(self.tube_name)
77
77
 
78
78
  {
79
79
  id: self.id,
80
80
  bkid: self.bkid,
81
81
  queue: queue_name,
82
- tube: expanded_tube_name,
82
+ tube: tube_name,
83
83
  watched: watched,
84
84
  beanstalk: self.bk.stats.to_h.symbolize_keys,
85
85
  }
@@ -1,12 +1,90 @@
1
1
  module Postburner
2
- # Send a mailer, tracked.
2
+ # A tracked job for sending ActionMailer emails via Beanstalkd.
3
3
  #
4
- # Postburner::Mailer.deliver(UserMailer, :welcome).with(user_id: 1)
5
- # Postburner::Mailer.deliver(UserMailer, :welcome).with(user_id: 1).queue! at: Time.current + 1.day
6
- # Postburner::Mailer.deliver(UserMailer, :welcome).with(user_id: 1).queue! delay: 5.minutes
4
+ # Provides a chainable API similar to ActionMailer for building and
5
+ # queueing email deliveries with full audit trail support.
6
+ #
7
+ # == Queue Configuration
8
+ #
9
+ # By default, mailer jobs use the +default_queue+ (typically 'default').
10
+ # You can route mailers to a dedicated queue using several approaches:
11
+ #
12
+ # === Via configuration (recommended)
13
+ #
14
+ # Set +default_mailer_queue+ in your postburner.yml or configuration:
15
+ #
16
+ # # config/postburner.yml
17
+ # production:
18
+ # default_mailer_queue: mailers
19
+ #
20
+ # # Or programmatically
21
+ # Postburner.configure do |config|
22
+ # config.default_mailer_queue = 'mailers'
23
+ # end
24
+ #
25
+ # === Via queue! option (per-job override)
26
+ #
27
+ # Override the queue when queueing a specific job:
28
+ #
29
+ # Postburner::Mailer.delivery(UserMailer, :welcome)
30
+ # .with(user_id: 1)
31
+ # .queue!(queue: 'priority_mailers')
32
+ #
33
+ # === Via instance setter (per-job override)
34
+ #
35
+ # Set the queue on the job instance before queueing:
36
+ #
37
+ # job = Postburner::Mailer.delivery(UserMailer, :welcome).with(user_id: 1)
38
+ # job.queue_name = 'priority_mailers'
39
+ # job.queue!
40
+ #
41
+ # @example Basic delivery (queued immediately)
42
+ # Postburner::Mailer.delivery(UserMailer, :welcome).with(user_id: 1).queue!
43
+ #
44
+ # @example Scheduled delivery at a specific time
45
+ # Postburner::Mailer.delivery(UserMailer, :welcome).with(user_id: 1).queue!(at: Time.current + 1.day)
46
+ #
47
+ # @example Delayed delivery
48
+ # Postburner::Mailer.delivery(UserMailer, :welcome).with(user_id: 1).queue!(delay: 5.minutes)
49
+ #
50
+ # @example Queue to specific queue
51
+ # Postburner::Mailer.delivery(UserMailer, :welcome).with(user_id: 1).queue!(queue: 'bulk_mailers')
52
+ #
53
+ # @see Postburner::Job Base class for tracked jobs
54
+ # @see Postburner::Configuration#default_mailer_queue Configuration option
7
55
  class Mailer < Job
8
- #queue 'mailers'
9
56
 
57
+ # Returns the queue name for this mailer job.
58
+ #
59
+ # Checks in order: instance override, +default_mailer_queue+ config,
60
+ # then falls back to +default_queue+.
61
+ #
62
+ # @return [String] Queue name
63
+ #
64
+ # @example Default behavior (uses default_mailer_queue or default_queue)
65
+ # job = Postburner::Mailer.delivery(UserMailer, :welcome)
66
+ # job.queue_name # => 'mailers' (if configured) or 'default'
67
+ #
68
+ # @example Instance override
69
+ # job = Postburner::Mailer.delivery(UserMailer, :welcome)
70
+ # job.queue_name = 'priority_mailers'
71
+ # job.queue_name # => 'priority_mailers'
72
+ def queue_name
73
+ @queue_name ||
74
+ self.class.queue ||
75
+ Postburner.configuration.default_mailer_queue ||
76
+ Postburner.configuration.default_queue
77
+ end
78
+
79
+ # Build a new mailer job without persisting.
80
+ #
81
+ # @param mailer [Class] The ActionMailer class (e.g., UserMailer)
82
+ # @param action [Symbol, String] The mailer action/method name (e.g., :welcome)
83
+ # @return [Postburner::Mailer] An unsaved mailer job instance
84
+ #
85
+ # @example
86
+ # job = Postburner::Mailer.delivery(UserMailer, :welcome)
87
+ # job.with(user_id: 1).queue!
10
88
  def self.delivery(mailer, action)
11
89
  job = self.new(
12
90
  args: {
@@ -17,14 +95,33 @@ module Postburner
17
95
  job
18
96
  end
19
97
 
98
+ # Build and persist a new mailer job.
99
+ #
100
+ # @param mailer [Class] The ActionMailer class (e.g., UserMailer)
101
+ # @param action [Symbol, String] The mailer action/method name (e.g., :welcome)
102
+ # @return [Postburner::Mailer] A persisted mailer job instance
103
+ #
104
+ # @example
105
+ # job = Postburner::Mailer.delivery!(UserMailer, :password_reset)
106
+ # job.with!(token: reset_token)
20
107
  def self.delivery!(mailer, action)
21
108
  job = self.delivery(mailer, action)
22
109
  job.save!
23
110
  job
24
111
  end
25
112
 
26
- # Similar to ActionMailer #with - set the parameters
113
+ # Set the mailer parameters without persisting.
114
+ #
115
+ # Similar to ActionMailer's +#with+ method for parameterized mailers.
116
+ # Parameters are serialized using ActiveJob::Arguments for safe storage.
117
+ #
118
+ # @param params [Hash] Parameters to pass to the mailer
119
+ # @return [self] Returns self for method chaining
27
120
  #
121
+ # @example
122
+ # Postburner::Mailer.delivery(UserMailer, :welcome)
123
+ # .with(user_id: 1, locale: :en)
124
+ # .queue!
28
125
  def with(params={})
29
126
  self.args.merge!(
30
127
  'params' => ::ActiveJob::Arguments.serialize(params)
@@ -32,16 +129,31 @@ module Postburner
32
129
  self
33
130
  end
34
131
 
132
+ # Set the mailer parameters and persist immediately.
133
+ #
134
+ # @param params [Hash] Parameters to pass to the mailer
135
+ # @return [self] Returns self for method chaining
136
+ #
137
+ # @example
138
+ # job = Postburner::Mailer.delivery!(OrderMailer, :confirmation)
139
+ # job.with!(order_id: order.id)
35
140
  def with!(params={})
36
141
  self.with(params)
37
142
  self.save!
38
143
  self
39
144
  end
40
145
 
41
- # Build the mail but don't send.
146
+ # Build the Mail::Message without sending.
147
+ #
148
+ # Useful for testing or inspecting the email before delivery.
42
149
  #
43
- # Optional `args` argument for testing convenience.
150
+ # @return [Mail::Message] The assembled email message
44
151
  #
152
+ # @example Inspect email before sending
153
+ # job = Postburner::Mailer.delivery(UserMailer, :welcome).with(user_id: 1)
154
+ # mail = job.assemble
155
+ # puts mail.subject # => "Welcome!"
156
+ # puts mail.to # => ["user@example.com"]
45
157
  def assemble
46
158
  mail = self.mailer.with(self.params).send(self.action)
47
159
  mail
@@ -49,22 +161,43 @@ module Postburner
49
161
 
50
162
  # Get the mailer class.
51
163
  #
164
+ # @return [Class] The ActionMailer class
165
+ #
166
+ # @example
167
+ # job.mailer # => UserMailer
52
168
  def mailer
53
169
  self.args['mailer'].constantize
54
170
  end
55
171
 
56
- # Get the mailer action as a symbol.
172
+ # Get the mailer action name.
173
+ #
174
+ # @return [Symbol, nil] The mailer action as a symbol
57
175
  #
176
+ # @example
177
+ # job.action # => :welcome
58
178
  def action
59
179
  self.args['action']&.to_sym
60
180
  end
61
181
 
62
- # Get the deserialized params.
182
+ # Get the deserialized mailer parameters.
63
183
  #
184
+ # @return [Hash] The parameters passed via {#with}
185
+ #
186
+ # @example
187
+ # job.params # => { user_id: 1, locale: :en }
64
188
  def params
65
189
  ::ActiveJob::Arguments.deserialize(self.args['params']).to_h
66
190
  end
67
191
 
192
+ # Execute the mailer job.
193
+ #
194
+ # Called by the Postburner worker. Assembles and delivers the email,
195
+ # logging progress at each step.
196
+ #
197
+ # @param args [Hash] Job arguments (unused, params stored in {#args})
198
+ # @return [void]
199
+ #
200
+ # @api private
68
201
  def perform(args)
69
202
  self.log! "Building"
70
203
  mail = self.assemble
@@ -22,7 +22,7 @@ module Postburner
22
22
  class Configuration
23
23
  # Global settings
24
24
  attr_accessor :beanstalk_url, :logger, :default_priority, :default_ttr
25
- attr_accessor :default_queue_name, :default_max_retries, :default_retry_delay
25
+ attr_accessor :default_queue, :default_mailer_queue, :default_max_retries, :default_retry_delay
26
26
  attr_accessor :default_scheduler_interval, :default_scheduler_priority
27
27
  attr_accessor :enqueue_options
28
28
 
@@ -34,6 +34,7 @@ module Postburner
34
34
  # @option options [Logger] :logger Logger instance (default: Rails.logger)
35
35
  # @option options [Integer] :default_priority Default job priority (default: 65536, lower = higher priority)
36
36
  # @option options [Integer] :default_ttr Default time-to-run in seconds (default: 300)
37
+ # @option options [String] :default_mailer_queue Queue name for Postburner::Mailer jobs (default: nil, uses default_queue)
37
38
  # @option options [Integer] :default_scheduler_interval Scheduler check interval in seconds (default: 300)
38
39
  # @option options [Integer] :default_scheduler_priority Scheduler job priority (default: 100)
39
40
  # @option options [Proc] :enqueue_options Proc that receives job and returns options hash
@@ -53,7 +54,8 @@ module Postburner
53
54
  @logger = options[:logger] || (defined?(Rails) ? Rails.logger : Logger.new(STDOUT))
54
55
  @default_priority = options[:default_priority] || 65536
55
56
  @default_ttr = options[:default_ttr] || 300
56
- @default_queue_name = options[:default_queue_name] || 'default'
57
+ @default_queue = options[:default_queue] || 'default'
58
+ @default_mailer_queue = options[:default_mailer_queue]
57
59
  @default_max_retries = options[:default_max_retries] || 0
58
60
  @default_retry_delay = options[:default_retry_delay] || ->(n) { 2 ** n }
59
61
  @default_scheduler_interval = options[:default_scheduler_interval] || 300
@@ -97,6 +99,7 @@ module Postburner
97
99
  # default: &default
98
100
  # beanstalk_url: <%= ENV.fetch('BEANSTALK_URL', 'beanstalk://localhost:11300') %>
99
101
  # default_priority: 131072 # change default priority from 65536 to 131072
102
+ # default_mailer_queue: mailers # optional: route Postburner::Mailer to separate queue
100
103
  #
101
104
  # production: # <- environment config, i.e. defaults
102
105
  # <<: *default
@@ -166,6 +169,8 @@ module Postburner
166
169
  beanstalk_url: env_config['beanstalk_url'],
167
170
  default_priority: env_config['default_priority'],
168
171
  default_ttr: env_config['default_ttr'],
172
+ default_queue: env_config['default_queue'],
173
+ default_mailer_queue: env_config['default_mailer_queue'],
169
174
  default_scheduler_interval: env_config['scheduler_interval'],
170
175
  default_scheduler_priority: env_config['scheduler_priority'],
171
176
  worker_config: worker_config
@@ -76,7 +76,7 @@ module Postburner
76
76
  #debugger
77
77
  Postburner::Job.transaction do
78
78
  Postburner.connected do |conn|
79
- tube_name = job.expanded_tube_name
79
+ tube_name = job.tube_name
80
80
  data = { class: job.class.name, args: [job.id] }
81
81
 
82
82
  # Get priority, TTR from job instance (respects instance overrides) or options
@@ -1,35 +1,96 @@
1
1
  module Postburner
2
+ # A wrapper around Beaneater tubes for inspecting Beanstalkd queues.
3
+ #
4
+ # Provides methods to enumerate tubes and paginate through jobs,
5
+ # primarily used by the Postburner web UI for job inspection.
6
+ #
7
+ # @note Beanstalkd is not designed for job enumeration or searching.
8
+ # It is optimized for fast job reservation, not inspection. This class
9
+ # iterates through job IDs sequentially which is inherently inefficient.
10
+ # Use this only for debugging and development purposes, not for
11
+ # production workloads or high-volume introspection of queues.
12
+ #
13
+ # @example List all tubes
14
+ # tubes = Postburner::Tube.all
15
+ # tubes.each { |tube| puts tube.peek_ids }
16
+ #
17
+ # @example Paginate through jobs in a tube
18
+ # tube = Postburner::Tube.all.first
19
+ # first_page = tube.jobs(20)
20
+ # second_page = tube.jobs(20, after: first_page.last.id)
21
+ #
22
+ # @see Postburner.connection For accessing the Beaneater connection
2
23
  class Tube
24
+ # Initialize a new Tube wrapper.
25
+ #
26
+ # @param tube [Beaneater::Tube] The underlying Beaneater tube object
3
27
  def initialize(tube)
4
28
  @tube = tube
5
29
  end
6
30
 
7
31
  # Get all tubes as Postburner::Tube instances.
8
32
  #
33
+ # @return [Array<Postburner::Tube>] All tubes from the Beanstalkd server
34
+ #
35
+ # @example
36
+ # Postburner::Tube.all
37
+ # # => [#<Postburner::Tube>, #<Postburner::Tube>, ...]
9
38
  def self.all
10
39
  Postburner.connection.tubes.all.map { |tube| self.new(tube) }
11
40
  end
12
41
 
13
- # Get all peeked ids across all known tubes.
42
+ # Get all peeked job IDs across all known tubes.
43
+ #
44
+ # Useful for finding the minimum known job ID when starting pagination.
45
+ #
46
+ # @return [Array<Integer>] Sorted array of job IDs from all tubes
14
47
  #
48
+ # @example
49
+ # Postburner::Tube.peek_ids
50
+ # # => [1, 5, 12, 47, 89, 102]
15
51
  def self.peek_ids
16
52
  self.all.map(&:peek_ids).flatten.sort
17
53
  end
18
54
 
19
- # Get all peeked ids.
55
+ # Get peeked job IDs from this tube.
20
56
  #
57
+ # Peeks at buried, ready, and delayed states to find known job IDs.
58
+ #
59
+ # @return [Array<Integer>] Job IDs visible via peek operations
60
+ #
61
+ # @example
62
+ # tube = Postburner::Tube.all.first
63
+ # ids = tube.peek_ids # => [1, 5, 12]
21
64
  def peek_ids
22
65
  [ :buried, :ready, :delayed ].map { |type| @tube.peek(type) }.
23
66
  reject(&:nil?).map(&:id).map(&:to_i)
24
67
  end
25
68
 
26
- # Get paginated array of jobs.
69
+ # Get a paginated array of jobs from this tube.
70
+ #
71
+ # Efficiently retrieves jobs by starting from known IDs and iterating
72
+ # until the requested count is fulfilled. Supports cursor-based pagination
73
+ # via the +after+ parameter.
74
+ #
75
+ # @param count [Integer] Number of jobs to return (default: 20)
76
+ # @param limit [Integer] Maximum ID range to scan (default: 1000)
77
+ # @param after [Integer, nil] Return jobs after this ID for pagination
78
+ # @return [Array<Beaneater::Job>] Jobs from this tube
27
79
  #
28
- # Attempts to do this efficiently as possible, by peeking at known
29
- # ids and exiting when count has been fulfilled.
80
+ # @example Get first page of jobs
81
+ # tube.jobs(20)
82
+ # # => [#<Beaneater::Job id=1>, #<Beaneater::Job id=5>, ...]
30
83
  #
31
- # Just pass the last known id to after for the next batch.
84
+ # @example Access job properties
85
+ # jobs = tube.jobs(5)
86
+ # jobs.first.id # => 1
87
+ # jobs.first.body # => "{\"job_class\":\"MyJob\",...}"
88
+ # jobs.first.stats # => {"tube"=>"default", "state"=>"ready", ...}
32
89
  #
90
+ # @example Paginate through jobs
91
+ # page1 = tube.jobs(20)
92
+ # page2 = tube.jobs(20, after: page1.last.id)
93
+ # # => [#<Beaneater::Job id=25>, #<Beaneater::Job id=26>, ...]
33
94
  def jobs(count=20, limit: 1000, after: nil)
34
95
  # Note: beaneater transforms hyphenated beanstalkd stats to underscores
35
96
  stats = @tube.stats
@@ -1,3 +1,3 @@
1
1
  module Postburner
2
- VERSION = '1.0.0.pre.17'
2
+ VERSION = '1.0.0.pre.18'
3
3
  end
data/lib/postburner.rb CHANGED
@@ -405,6 +405,11 @@ module Postburner
405
405
  # Postburner.clear_jobs!(Postburner.scheduler_tube_name)
406
406
  # # Clears the scheduler watchdog tube
407
407
  #
408
+ # @example Clear watched tubes AND scheduler tube
409
+ # Postburner.clear_jobs!(Postburner.watched_tube_names + [Postburner.scheduler_tube_name])
410
+ # # Or use the convenience method:
411
+ # Postburner.clear_all!
412
+ #
408
413
  # @example Trying to clear unconfigured tube - RAISES ERROR
409
414
  # Postburner.clear_jobs!(['some-other-app-tube'])
410
415
  # # => ArgumentError: Cannot clear tubes not in configuration
@@ -413,6 +418,7 @@ module Postburner
413
418
  # result = Postburner.clear_jobs!(Postburner.watched_tube_names, silent: true)
414
419
  # result[:totals][:total] # => 42
415
420
  #
421
+ # @see #clear_all!
416
422
  # @see Connection#clear_tubes!
417
423
  #
418
424
  def self.clear_jobs!(tube_names = nil, silent: false)
@@ -431,21 +437,25 @@ module Postburner
431
437
  # Clears all configured tubes including scheduler.
432
438
  #
433
439
  # Convenience method that clears all watched tubes plus the scheduler tube
434
- # in a single call. This is the equivalent of:
440
+ # in a single call. Use this to completely reset Postburner's Beanstalkd state,
441
+ # such as during test setup or when recovering from a stuck state.
435
442
  #
443
+ # Equivalent to:
436
444
  # Postburner.clear_jobs!(Postburner.watched_tube_names + [Postburner.scheduler_tube_name])
437
445
  #
438
446
  # @param silent [Boolean] If true, suppress output to stdout (default: false)
439
447
  #
440
448
  # @return [Hash] Statistics and results (see Connection#clear_tubes!)
441
449
  #
442
- # @example Clear everything
450
+ # @example Clear everything (interactive/console use)
443
451
  # Postburner.clear_all!
444
452
  #
445
- # @example Silent mode
446
- # result = Postburner.clear_all!(silent: true)
453
+ # @example Silent mode (test setup or programmatic use)
454
+ # Postburner.clear_all!(silent: true)
447
455
  #
448
456
  # @see #clear_jobs!
457
+ # @see #watched_tube_names
458
+ # @see #scheduler_tube_name
449
459
  #
450
460
  def self.clear_all!(silent: false)
451
461
  all_tubes = watched_tube_names + [scheduler_tube_name]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postburner
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.17
4
+ version: 1.0.0.pre.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Smith