queuel 0.1.0 → 0.2.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.
@@ -0,0 +1 @@
1
+ service_name: travis-ci
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- queuel (0.1.0)
4
+ queuel (0.2.0)
5
5
  mono_logger
6
+ multi_json
6
7
  thread
7
8
 
8
9
  GEM
@@ -11,6 +12,13 @@ GEM
11
12
  cane (2.5.2)
12
13
  parallel
13
14
  coderay (1.0.9)
15
+ colorize (0.5.8)
16
+ coveralls (0.6.7)
17
+ colorize
18
+ multi_json (~> 1.3)
19
+ rest-client
20
+ simplecov (>= 0.7)
21
+ thor
14
22
  diff-lcs (1.2.4)
15
23
  ethon (0.5.12)
16
24
  ffi (>= 1.3.0)
@@ -46,7 +54,7 @@ GEM
46
54
  lumberjack (1.0.3)
47
55
  method_source (0.8.1)
48
56
  mime-types (1.23)
49
- mono_logger (1.0.1)
57
+ mono_logger (1.1.0)
50
58
  multi_json (1.7.3)
51
59
  parallel (0.6.5)
52
60
  pry (0.9.12.2)
@@ -97,6 +105,7 @@ PLATFORMS
97
105
  DEPENDENCIES
98
106
  bundler (~> 1.3)
99
107
  cane
108
+ coveralls
100
109
  guard-bundler
101
110
  guard-cane
102
111
  guard-rspec
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Queuel
2
2
  [![Gem Version](https://badge.fury.io/rb/queuel.png)](http://badge.fury.io/rb/queuel)
3
3
  [![Build Status](https://travis-ci.org/sportngin/queuel.png?branch=master)](https://travis-ci.org/sportngin/queuel)
4
+ [![Code Climate](https://codeclimate.com/github/sportngin/queuel.png)](https://codeclimate.com/github/sportngin/queuel)
5
+ [![Coverage Status](https://coveralls.io/repos/sportngin/queuel/badge.png)](https://coveralls.io/r/sportngin/queuel)
4
6
 
5
7
  Queuel is a kewl, lite wrapper around Queue interfaces. Currently it implements:
6
8
 
@@ -52,6 +54,13 @@ Queuel.configure do
52
54
  logger Logger # default: MonoLogger.new(STDOUT)
53
55
 
54
56
  log_level MonoLogger::DEBUG # default: MonoLogger::ERROR # => 3
57
+
58
+ # Incoming messages can be automatically encoded/decoded
59
+ decode_by_default false # default: true
60
+ decoder ->(body) { MultiJson.load body } # default: Queuel::Serialization::Json::Decoder
61
+
62
+ encode_by_default false # default: true
63
+ encoder ->(body) { body.to_s } # default: Queuel::Serialization::Json::Encoder
55
64
  end
56
65
  ```
57
66
 
@@ -88,15 +97,40 @@ end
88
97
 
89
98
  ```ruby
90
99
  message.id # => ID of the message
91
- message.body # => Message body
100
+ message.raw_body # => Raw Message body
101
+ message.body # => Message body (parsed, if configured to do so)
92
102
  message.delete # => Delete the message
93
103
  ```
94
104
 
95
- ## TODO
105
+ #### Parsing
106
+
107
+ Queuel uses [MultiJson](https://github.com/intridea/multi_json) to provide
108
+ some auto-message decoding/encodeing features. With MultiJson you may install your own engine
109
+ (like [Oj](https://github.com/ohler55/oj)).
110
+
111
+ Because of the parsing given, you will default to encoding and decoding JSON:
112
+
113
+ ```ruby
114
+ Queuel.push username: "jon"
115
+ Queuel.pop # => { username: "jon" }
116
+ ```
117
+
118
+ You can configure your decoder/encoder on the fly:
119
+
120
+ ```ruby
121
+ Queuel.push { username: "jon" }, encoder: ->(body) { }
122
+ Queuel.pop decoder: ->(raw) { }
123
+ Queuel.receive decoder: ->(raw) { }
124
+ ```
125
+
126
+ You can turn of encoding/decoding at calltime with:
127
+
128
+ ```ruby
129
+ Queuel.push { username: "jon" }, encode: false
130
+ Queuel.pop decode: false
131
+ Queuel.receive decode: false
132
+ ```
96
133
 
97
- * Implement AMQP
98
- * Configureable exponential back-off on `receive`
99
- * Provide a Daemon
100
134
 
101
135
  ## Contributing
102
136
 
@@ -1,5 +1,7 @@
1
1
  require "queuel/version"
2
2
  require "forwardable"
3
+ require "queuel/core_ext/hash"
4
+ require "queuel/serialization/json"
3
5
  require "queuel/configurator"
4
6
  require "queuel/introspect"
5
7
 
@@ -24,8 +26,15 @@ module Queuel
24
26
  extend Introspect
25
27
  class << self
26
28
  extend Forwardable
27
- def_delegators :client, :push, :pop, :receive, :with
28
- def_delegators :config, :credentials, :default_queue, :receiver_threads
29
+ def_delegators :client, :peek, :push, :pop, :receive, :with
30
+ def_delegators :config,
31
+ :credentials,
32
+ :default_queue,
33
+ :receiver_threads,
34
+ :decode_by_default?,
35
+ :decoder,
36
+ :encode_by_default?,
37
+ :encoder
29
38
  alias << pop
30
39
  end
31
40
 
@@ -1,16 +1,41 @@
1
+ require 'forwardable'
1
2
  module Queuel
2
3
  module Base
3
4
  class Message
4
- def initialize(message_object)
5
+ extend Forwardable
6
+ private
7
+ def_delegators :Queuel,
8
+ :decode_by_default?,
9
+ :encode_by_default?
10
+ attr_accessor :message_object
11
+ attr_accessor :options
12
+ attr_writer :id
13
+ attr_writer :queue
14
+
15
+ public
16
+
17
+ attr_reader :id
18
+ attr_writer :body
19
+ attr_accessor :raw_body
20
+ attr_reader :queue
21
+
22
+ # @argument message_object
23
+ # @argument options hash
24
+ def initialize(message_object = nil, options = {})
5
25
  self.message_object = message_object
26
+ self.options = options
6
27
  end
7
28
 
8
29
  def delete
9
30
  raise NotImplementedError, "must define method #delete"
10
31
  end
11
32
 
33
+ def body
34
+ @body || decoded_raw_body
35
+ end
36
+
12
37
  def empty?
13
- body.to_s.empty?
38
+ raw_body.to_s.empty?
14
39
  end
15
40
  alias blank? empty?
16
41
 
@@ -18,15 +43,39 @@ module Queuel
18
43
  !empty?
19
44
  end
20
45
 
21
- attr_reader :id
22
- attr_reader :body
23
- attr_reader :queue
24
-
25
46
  private
26
- attr_accessor :message_object
27
- attr_writer :id
28
- attr_writer :body
29
- attr_writer :queue
47
+
48
+ def decoder
49
+ options[:decoder] || Queuel.decoder
50
+ end
51
+
52
+ def encoder
53
+ options[:encoder] || Queuel.encoder
54
+ end
55
+
56
+ def encode?
57
+ options.fetch(:encode) { encode_by_default? }
58
+ end
59
+
60
+ def decode?
61
+ options.fetch(:decode) { decode_by_default? }
62
+ end
63
+
64
+ def decoded_raw_body
65
+ decode_body? ? decoder.call(raw_body) : raw_body
66
+ end
67
+
68
+ def encoded_body
69
+ encode_body? ? encoder.call(body) : body
70
+ end
71
+
72
+ def encode_body?
73
+ !@body.to_s.empty? && !encoder.nil? && encode?
74
+ end
75
+
76
+ def decode_body?
77
+ !decoder.nil? && decode? && raw_body.is_a?(String)
78
+ end
30
79
  end
31
80
  end
32
81
  end
@@ -12,14 +12,15 @@ module Queuel
12
12
  raise NotImplementedError, "must implement #peek"
13
13
  end
14
14
 
15
- def push(message)
15
+ def push(message, options = {})
16
16
  raise NotImplementedError, "must implement #push"
17
17
  end
18
18
 
19
19
  def pop(options = {}, &block)
20
- bare_message = pop_bare_message(options)
20
+ message_options, engine_options = Queuel::Hash.new(options).partition { |(k,_)| message_option_keys.include? k.to_s }
21
+ bare_message = pop_bare_message(engine_options)
21
22
  unless bare_message.nil?
22
- build_new_message(bare_message).tap { |message|
23
+ build_new_message(bare_message, message_options).tap { |message|
23
24
  if block_given? && message.present?
24
25
  message.delete if yield(message)
25
26
  end
@@ -35,6 +36,16 @@ module Queuel
35
36
  attr_accessor :client
36
37
  attr_accessor :name
37
38
 
39
+ def message_option_keys
40
+ %w[encode encoder decode decoder]
41
+ end
42
+
43
+ def build_push_message(message, options = {})
44
+ message_klass.new(nil, options).tap { |m|
45
+ m.body = message
46
+ }.raw_body
47
+ end
48
+
38
49
  def thread_count
39
50
  Queuel.receiver_threads || 1
40
51
  end
@@ -43,8 +54,8 @@ module Queuel
43
54
  raise NotImplementedError, "must implement bare Message getter"
44
55
  end
45
56
 
46
- def build_new_message(bare_message)
47
- message_klass.new(bare_message)
57
+ def build_new_message(bare_message, options = {})
58
+ message_klass.new(bare_message, options)
48
59
  end
49
60
 
50
61
  def message_klass
@@ -7,7 +7,7 @@ module Queuel
7
7
  self.given_queue = init_queue
8
8
  end
9
9
 
10
- [:push, :pop, :receive].each do |operation|
10
+ [:peek, :push, :pop, :receive].each do |operation|
11
11
  define_method(operation) do |*args, &block|
12
12
  with_queue { queue_connection.public_send(operation, *args, &block) }
13
13
  end
@@ -9,11 +9,12 @@ module Queuel
9
9
  @option_values ||= {}
10
10
  end
11
11
 
12
- def self.define_param_accessors(param_name)
12
+ def self.define_param_accessors(param_name, boolean = false)
13
13
  define_method param_name do |*values|
14
14
  value = values.first
15
15
  value ? self.send("#{param_name}=", value) : retrieve(param_name)
16
16
  end
17
+ define_method("#{param_name}?") { !!retrieve(param_name) } if boolean
17
18
  define_method "#{param_name}=" do |value|
18
19
  validate!(param_name, value) &&
19
20
  instance_variable_set("@#{param_name}", value)
@@ -42,7 +43,7 @@ module Queuel
42
43
  def self.param(param_name, options = {})
43
44
  attr_accessor param_name
44
45
  self.option_values[param_name] = options
45
- define_param_accessors param_name
46
+ define_param_accessors param_name, options[:boolean]
46
47
  public param_name
47
48
  public "#{param_name}="
48
49
  end
@@ -53,6 +54,17 @@ module Queuel
53
54
  param :engine
54
55
  param :default_queue
55
56
  param :receiver_threads, default: 1
57
+
58
+ param :decode_by_default, default: true, boolean: true
59
+ param :decoder, default: Queuel::Serialization::Json::Decoder, validate: {
60
+ validator: ->(encoder) { encoder.respond_to?(:call) }
61
+ }
62
+
63
+ param :encode_by_default, default: true, boolean: true
64
+ param :encoder, default: Queuel::Serialization::Json::Encoder, validate: {
65
+ validator: ->(encoder) { encoder.respond_to?(:call) }
66
+ }
67
+
56
68
  param :logger, default: MonoLogger.new(STDOUT), validate: {
57
69
  message: "Logger must respond to #{%w[info warn debug level level]}",
58
70
  validator: ->(logger) {
@@ -0,0 +1,20 @@
1
+ module Queuel
2
+ class Hash < ::Hash
3
+ def self.new(*args, &block)
4
+ if args.first.is_a?(::Hash)
5
+ allocate.send(:initialize).replace(args.first)
6
+ else
7
+ super *args, &block
8
+ end
9
+ end
10
+
11
+ def partition(&block)
12
+ if block_given?
13
+ one, two = super &block
14
+ [Hash[one], Hash[two]]
15
+ else
16
+ super &block
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,11 +1,10 @@
1
- require 'forwardable'
2
1
  module Queuel
3
2
  module IronMq
4
3
  class Message < Base::Message
5
- extend Forwardable
6
-
7
- def body
8
- @body || message_object && message_object.body
4
+ def raw_body
5
+ @raw_body ||
6
+ (message_object && message_object.body) ||
7
+ encoded_body
9
8
  end
10
9
 
11
10
  def delete
@@ -12,8 +12,8 @@ module Queuel
12
12
  end
13
13
 
14
14
  # For IronMQ it should just be (message)
15
- def push(message)
16
- queue_connection.post message
15
+ def push(message, options = {})
16
+ queue_connection.post build_push_message(message, options)
17
17
  end
18
18
 
19
19
  private
@@ -6,7 +6,7 @@ module Queuel
6
6
  []
7
7
  end
8
8
 
9
- def push(message)
9
+ def push(message, options = {})
10
10
  end
11
11
 
12
12
  # Nullify
@@ -0,0 +1,37 @@
1
+ require 'multi_json'
2
+ module Queuel
3
+ module Serialization
4
+ module Json
5
+ SerializationError = Class.new StandardError
6
+ class Decoder
7
+ def self.call(body)
8
+ new(body).decode
9
+ end
10
+
11
+ def initialize(body)
12
+ @body = body.to_s
13
+ end
14
+
15
+ def decode
16
+ MultiJson.load @body, symbolize_keys: true
17
+ rescue MultiJson::LoadError
18
+ raise SerializationError, "Error reading:\n#{@body}"
19
+ end
20
+ end
21
+
22
+ class Encoder
23
+ def self.call(body)
24
+ new(body).encode
25
+ end
26
+
27
+ def initialize(body)
28
+ @body = body
29
+ end
30
+
31
+ def encode
32
+ MultiJson.dump @body
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,3 +1,3 @@
1
1
  module Queuel
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -32,7 +32,9 @@ Gem::Specification.new do |spec|
32
32
  spec.add_development_dependency "rb-fchange"
33
33
  spec.add_development_dependency "cane"
34
34
  spec.add_development_dependency "json", "~> 1.7.7"
35
+ spec.add_development_dependency "coveralls"
35
36
 
36
37
  spec.add_dependency "thread"
37
38
  spec.add_dependency "mono_logger"
39
+ spec.add_dependency "multi_json"
38
40
  end
@@ -5,12 +5,70 @@ module Queuel
5
5
  it_should_behave_like "a message"
6
6
  describe "initialization with Iron Object" do
7
7
  let(:queue_double) { double "Queue" }
8
- let(:message_object) { double "IronMessage", id: 1, body: "body", queue: queue_double }
8
+ let(:body) { "body" }
9
+ let(:message_object) { double "IronMessage", id: 1, body: body, queue: queue_double }
9
10
  subject { described_class.new(message_object) }
10
11
 
12
+ before do
13
+ subject.stub decode_body?: false
14
+ end
15
+
11
16
  its(:id) { should == 1 }
12
17
  its(:body) { should == "body" }
13
18
  its(:queue) { should == queue_double }
19
+
20
+ describe "with json" do
21
+ let(:body) { '{"username":"jon"}' }
22
+ before do
23
+ subject.stub decode_body?: true
24
+ end
25
+
26
+ its(:body) { should == { username: "jon" } }
27
+ its(:raw_body) { should == body }
28
+ end
29
+ end
30
+
31
+ describe "using message for encoding" do
32
+ subject { described_class.new }
33
+
34
+ describe "setting the body" do
35
+ let(:hash_json) { { username: "jon" } }
36
+ let(:string_json) { '{"username":"jon"}' }
37
+
38
+ before do
39
+ subject.body = body
40
+ end
41
+
42
+ describe "valid json hash" do
43
+ let(:body) { hash_json }
44
+
45
+ its(:body) { should == hash_json }
46
+ its(:raw_body) { should == string_json }
47
+ end
48
+ end
49
+
50
+ describe "setting the raw body" do
51
+ let(:hash_json) { { username: "jon" } }
52
+ let(:string_json) { '{"username":"jon"}' }
53
+
54
+ before do
55
+ subject.raw_body = raw_body
56
+ end
57
+
58
+ describe "valid json string" do
59
+ let(:raw_body) { string_json }
60
+
61
+ its(:body) { should == hash_json }
62
+ its(:raw_body) { should == raw_body }
63
+ end
64
+
65
+ describe "valid json hash" do
66
+ let(:raw_body) { hash_json }
67
+
68
+ its(:body) { should == hash_json }
69
+ its(:raw_body) { should == raw_body }
70
+ end
71
+ end
14
72
  end
15
73
  end
16
74
  end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ module Queuel
3
+ module Serialization
4
+ module Json
5
+ describe Decoder do
6
+ let(:body) { '{"name":"jon"}' }
7
+ subject { described_class.new body }
8
+
9
+ it "can call from the class" do
10
+ described_class.call(body).should == { name: "jon" }
11
+ end
12
+
13
+ it "can decode from instance" do
14
+ subject.decode.should == { name: "jon" }
15
+ end
16
+
17
+ describe "with bad body" do
18
+ let(:body) { '{"name":"jon"' }
19
+
20
+ it "fails on bad body" do
21
+ expect { subject.decode }.to raise_error SerializationError
22
+ end
23
+ end
24
+ end
25
+
26
+ describe Encoder do
27
+ let(:encoded_body) { '{"name":"jon"}' }
28
+ let(:body) { { "name" => "jon" } }
29
+ subject { described_class.new body }
30
+
31
+ it "can call from the class" do
32
+ described_class.call(body).should == encoded_body
33
+ end
34
+
35
+ it "can decode from instance" do
36
+ subject.encode.should == encoded_body
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -4,6 +4,8 @@
4
4
  # loaded once.
5
5
  #
6
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ require 'coveralls'
8
+ Coveralls.wear!
7
9
  require 'simplecov'
8
10
  SimpleCov.start do
9
11
  add_filter '/spec/'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: queuel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-20 00:00:00.000000000 Z
12
+ date: 2013-05-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -235,6 +235,22 @@ dependencies:
235
235
  - - ~>
236
236
  - !ruby/object:Gem::Version
237
237
  version: 1.7.7
238
+ - !ruby/object:Gem::Dependency
239
+ name: coveralls
240
+ requirement: !ruby/object:Gem::Requirement
241
+ none: false
242
+ requirements:
243
+ - - ! '>='
244
+ - !ruby/object:Gem::Version
245
+ version: '0'
246
+ type: :development
247
+ prerelease: false
248
+ version_requirements: !ruby/object:Gem::Requirement
249
+ none: false
250
+ requirements:
251
+ - - ! '>='
252
+ - !ruby/object:Gem::Version
253
+ version: '0'
238
254
  - !ruby/object:Gem::Dependency
239
255
  name: thread
240
256
  requirement: !ruby/object:Gem::Requirement
@@ -267,6 +283,22 @@ dependencies:
267
283
  - - ! '>='
268
284
  - !ruby/object:Gem::Version
269
285
  version: '0'
286
+ - !ruby/object:Gem::Dependency
287
+ name: multi_json
288
+ requirement: !ruby/object:Gem::Requirement
289
+ none: false
290
+ requirements:
291
+ - - ! '>='
292
+ - !ruby/object:Gem::Version
293
+ version: '0'
294
+ type: :runtime
295
+ prerelease: false
296
+ version_requirements: !ruby/object:Gem::Requirement
297
+ none: false
298
+ requirements:
299
+ - - ! '>='
300
+ - !ruby/object:Gem::Version
301
+ version: '0'
270
302
  description: Light Queue wrapper tool
271
303
  email:
272
304
  - j.phenow@gmail.com
@@ -275,6 +307,7 @@ extensions: []
275
307
  extra_rdoc_files: []
276
308
  files:
277
309
  - .cane
310
+ - .coveralls.yml
278
311
  - .gitignore
279
312
  - .irbrc
280
313
  - .rspec
@@ -295,6 +328,7 @@ files:
295
328
  - lib/queuel/base/queue.rb
296
329
  - lib/queuel/client.rb
297
330
  - lib/queuel/configurator.rb
331
+ - lib/queuel/core_ext/hash.rb
298
332
  - lib/queuel/introspect.rb
299
333
  - lib/queuel/iron_mq/engine.rb
300
334
  - lib/queuel/iron_mq/message.rb
@@ -304,6 +338,7 @@ files:
304
338
  - lib/queuel/null/message.rb
305
339
  - lib/queuel/null/poller.rb
306
340
  - lib/queuel/null/queue.rb
341
+ - lib/queuel/serialization/json.rb
307
342
  - lib/queuel/version.rb
308
343
  - queuel.gemspec
309
344
  - spec/lib/queuel/base/queue_spec.rb
@@ -317,6 +352,7 @@ files:
317
352
  - spec/lib/queuel/null/message_spec.rb
318
353
  - spec/lib/queuel/null/poller_spec.rb
319
354
  - spec/lib/queuel/null/queue_spec.rb
355
+ - spec/lib/queuel/serialization/json_spec.rb
320
356
  - spec/lib/queuel_spec.rb
321
357
  - spec/spec_helper.rb
322
358
  - spec/support/engine_shared_example.rb
@@ -339,7 +375,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
339
375
  version: '0'
340
376
  segments:
341
377
  - 0
342
- hash: 666822157956022088
378
+ hash: 3833538676296984811
343
379
  required_rubygems_version: !ruby/object:Gem::Requirement
344
380
  none: false
345
381
  requirements:
@@ -348,7 +384,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
348
384
  version: '0'
349
385
  segments:
350
386
  - 0
351
- hash: 666822157956022088
387
+ hash: 3833538676296984811
352
388
  requirements: []
353
389
  rubyforge_project:
354
390
  rubygems_version: 1.8.25
@@ -367,6 +403,7 @@ test_files:
367
403
  - spec/lib/queuel/null/message_spec.rb
368
404
  - spec/lib/queuel/null/poller_spec.rb
369
405
  - spec/lib/queuel/null/queue_spec.rb
406
+ - spec/lib/queuel/serialization/json_spec.rb
370
407
  - spec/lib/queuel_spec.rb
371
408
  - spec/spec_helper.rb
372
409
  - spec/support/engine_shared_example.rb