queuel 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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