la_gear 1.0.0.pre.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d34135664854b75f8889357557d68ad4b482515
4
+ data.tar.gz: 802e22ef6a102736e54b244fa5eb38d74028fd96
5
+ SHA512:
6
+ metadata.gz: a229175adb0a558e1acf8d5f9864a2e0e73a39ce51f8764dfffb8293ff561a5d23b9bd011b015a03b84a279eb3abf4de9f4b6f7d8251843f002b0d92978e4e53
7
+ data.tar.gz: 429397c8f840c1a0f1e84f32c4fdaa7483a301d558f663415ab7069b7e27dcc6f5c4b406873bc2b8409c923eec3e661208c332c2240b4b369b08ff728df6427e
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.rubocop.yml ADDED
@@ -0,0 +1,12 @@
1
+ AllCops:
2
+ Include:
3
+ Exclude:
4
+
5
+ LineLength:
6
+ Max: 99
7
+
8
+ Documentation:
9
+ Enabled: false
10
+
11
+ GlobalVars:
12
+ AllowedVariables: [$publisher]
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in la_gear.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Gabriel Chaney
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # L.A. Gear
2
+
3
+ This gem has two primary purposes:
4
+ * Allowing you to define one worker class that has your [`sneakers`](https://github.com/jondot/sneakers) worker queue configuration and the [`sidekiq`](https://github.com/mperham/sidekiq) `perform` method that actually processes the message.
5
+ * DRYing up your `sneakers` configuration by using a conventions.
6
+
7
+ Here are some other features:
8
+ * Messages are deserialized as JSON by default and the properties are passed to Sidekiq's `perform_async` method
9
+ * Out-of-the-box configuration of `sneakers` for multiple RabbitMQ subscribers
10
+ * An alternative to the `Sneakers::Publisher#publish` method that allows you to pass in more options when publishing to a RabbitMQ exchange
11
+ * Helper methods for configuring `sneakers`
12
+ * Support for configuring `sneakers` with [BigWig](http://bigwig.io/)
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ gem 'la_gear'
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install la_gear
27
+
28
+ ## Usage
29
+
30
+ Instead of `include Sneakers::Worker` in your workers, use `include LaGear::Worker`. Then all of your queues and routing key bindings will automatically use an `Sneakers::Config[:app_name]` plus the class name of your worker as your queue name and the class name of your worker as the routing key. For example,
31
+ ```
32
+ Sneakers.configure app_name: 'pogs_are_awesome'
33
+
34
+ class BoKnows
35
+ include LaGear::Worker
36
+
37
+ def perform(baseball, football)
38
+ # ... message processing code ...
39
+ end
40
+ end
41
+ ```
42
+ would by default result in the following 2 queue names: `pogs_are_awesome.bo_knows` and `pogs_are_awesome.bo_knows.retry`. The default routing key would be: `bo_knows`. This allows for multiple consumer queue bindings for a direct exchange (see the Multiple Bindings section of https://www.rabbitmq.com/tutorials/tutorial-four-ruby.html). The `.retry` is for if you are using [dead letter exchanges](https://www.rabbitmq.com/dlx.html) and [message expirations](https://www.rabbitmq.com/ttl.html) to perform retries (which you should). Just call the `Sneakers::Worker#from_queue` method to override the convention-based defaults.
43
+
44
+ Also, notice in the example above that we don't define a `work` method for `Sneakers::Worker`. That's because `LaGear::Worker` defines it for you. All the method does deserialize your RabbitMQ message (defaults to JSON) and pass each of the properties as a parameters to Sidekiq's `perform_async` method to process the message. Put your message processing code in a `perform` method instead. That's the method that Sidekiq invokes when it actual processes the message that `LaGear::Worker#work` sent to it.
45
+
46
+ ### The Bus
47
+
48
+ There is also a `LaGear::Bus` class which is an alternative to the `Sneakers::Publisher`. It adds an `opts` parameter to the `publish` method, allowing you to pass the options you would pass to the `bunny` exchange publish [method](http://reference.rubybunny.info/Bunny/Exchange.html#publish-instance_method) (see http://reference.rubybunny.info/Bunny/Exchange.html#publish-instance_method for the list of options).
49
+
50
+ `LaGear::Bus` also has support for BigWig's [bi-endpoint]((https://devcenter.heroku.com/articles/rabbitmq-bigwig#provisioning-the-add-on)) configuration where they use one RabbitMQ endpoint for receiving and one for transmitting. Configure Sneaker with an `amqp` and `amqp_publish` to get this working. Or you can just use the `LaGear::SneakersConfigurer.configure_bi_amqp_endpoints` convience method. Here's an example for a Heroku app:
51
+ ```
52
+ if ENV['RABBITMQ_BIGWIG_RX_URL'] && ENV['RABBITMQ_BIGWIG_TX_URL']
53
+ LaGear::SneakersConfigurer.configure_bi_amqp_endpoints(
54
+ ENV['RABBITMQ_BIGWIG_RX_URL'],
55
+ ENV['RABBITMQ_BIGWIG_TX_URL']
56
+ )
57
+ end
58
+ # else Sneakers/Bunny just uses the default RabbitMQ endpoint (amqp://localhost:15672)
59
+ ```
60
+
61
+ The reason I call it a "bus" is because I want to end up adding bus-style semantic methods to it besides publish similar to the methods in NServiceBus's [`IBus`](https://github.com/Particular/NServiceBus/blob/develop/src/NServiceBus.Core/IBus.cs) (see also [`ISendOnlyBus`](https://github.com/Particular/NServiceBus/blob/develop/src/NServiceBus.Core/ISendOnlyBus.cs)).
62
+
63
+ ## Known Issues
64
+
65
+ * For workers in a Rails 3.2 app I have, I had to explicilty include `Sneakers::Worker` and `Sidekiq::Worker` before `LaGear::Worker` because of an issue with it complaing that the `include` method was private in the Rails environment (see [here](https://github.com/gabrieljoelc/la_gear/blob/master/lib/la_gear/worker.rb#L6) for the current workaround). Here's what your workers should look like if this is happening to you:
66
+ ```
67
+ class BoKnows
68
+ include Sneakers::Worker unless LaGear::Worker.sidekiq_proc?
69
+ include Sidekiq::Worker
70
+ include LaGear::Worker
71
+
72
+ def perform(baseball, football)
73
+ # ... your worker code ...
74
+ end
75
+ end
76
+ ```
77
+
78
+ ## Contributing
79
+
80
+ 1. Fork it ( https://github.com/[my-github-username]/la_gear/fork )
81
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
82
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
83
+ 4. Push to the branch (`git push origin my-new-feature`)
84
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/la_gear.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'la_gear/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'la_gear'
8
+ spec.version = LaGear::VERSION
9
+ spec.authors = ['Gabriel Chaney']
10
+ spec.email = ['gabriel.chaney@gmail.com']
11
+ spec.summary = %q{A thin abstraction on-top-of [sneakers](https://github.com/jondot/sneakers) to DRY your workers. Pump it up!}
12
+ #spec.description = %q{This gem allows you to DRY up your [sneakers](https://github.com/jondot/sneakers) workers by using a conventions-based configuration. It also includes a `LaGear::Bus` class that allows you to pass in more options when publishing to an exchange.}
13
+ spec.homepage = 'https://github.com/giftcardzen/la_gear'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/).reject{|f| f == 'Gemfile.lock'}
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+ spec.add_dependency 'json', '~> 1.8'
21
+ spec.add_dependency 'bunny', '~> 1.6.3'
22
+ spec.add_dependency 'sneakers', '~> 1.0'
23
+ spec.add_dependency 'activesupport', '~> 4.2'
24
+ spec.add_dependency 'sidekiq', '~> 3.3'
25
+ spec.add_dependency 'connection_pool', '~> 2.1'
26
+
27
+ spec.add_development_dependency 'rake', '~> 10.4'
28
+ end
@@ -0,0 +1,23 @@
1
+ module LaGear
2
+ module ActiveRecord
3
+ module PublishTriggerable
4
+ def publish_after_commit(routing_key, opts)
5
+ publish_method = "publish_#{routing_key}"
6
+ define_method publish_method do
7
+ message = block_given? ? yield(self) : {}
8
+ Bus.publish(routing_key.to_s, message)
9
+ end
10
+ after_commit publish_method, opts
11
+ end
12
+
13
+ def send_after_commit(routing_key, opts)
14
+ publish_method = "publish_#{routing_key}"
15
+ define_method publish_method do
16
+ message = block_given? ? yield(self) : {}
17
+ Bus.publish_local(routing_key.to_s, message)
18
+ end
19
+ after_commit publish_method, opts
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1 @@
1
+ require 'la_gear/active_record/publish_triggerable'
@@ -0,0 +1,50 @@
1
+ module LaGear
2
+ module Bus
3
+ def init_pool(size = ::Sidekiq.options[:concurrency],
4
+ timeout = 3)
5
+ $publisher = ConnectionPool.new(
6
+ size: size,
7
+ timeout: timeout
8
+ ) { ::LaGear::Publisher.new }
9
+
10
+ $publisher.with do |bus|
11
+ puts "Has the bus arrived? #{bus.send(:serialize, ❨╯°□°❩╯︵┻━┻: 'Calm down, bro')}"
12
+ end
13
+ end
14
+ module_function :init_pool
15
+
16
+ def publish(routing_key, msg, bunny_opts = {}, sidekiq_opts = {})
17
+ DelayablePublisher.sidekiq_delay(sidekiq_opts).publish(routing_key, msg, bunny_opts)
18
+ end
19
+ module_function :publish
20
+
21
+ def publish_in(interval, routing_key, msg, bunny_opts = {}, sidekiq_opts = {})
22
+ DelayablePublisher.sidekiq_delay_for(interval, sidekiq_opts).publish(routing_key, msg, bunny_opts)
23
+ end
24
+ module_function :publish_in
25
+
26
+ def publish_at(timestamp, routing_key, msg, bunny_opts = {}, sidekiq_opts = {})
27
+ DelayablePublisher.sidekiq_delay_until(timestamp, sidekiq_opts).publish(routing_key, msg, bunny_opts)
28
+ end
29
+ module_function :publish_at
30
+
31
+ def publish_local(routing_key, msg)
32
+ routing_key.classify.constantize.perform_async(*msg.values)
33
+ end
34
+ module_function :publish_local
35
+
36
+ private
37
+
38
+ class DelayablePublisher
39
+ def self.publish(routing_key, msg, opts = {})
40
+ if $publisher
41
+ $publisher.with do |publisher|
42
+ publisher.publish(routing_key, msg, opts)
43
+ end
44
+ else # this is what integration tests might use
45
+ LaGear::Publisher.new.publish(routing_key, msg, opts)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,4 @@
1
+ module LaGear
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,39 @@
1
+ module LaGear
2
+ class Publisher < ::Sneakers::Publisher
3
+ attr_accessor :exchange
4
+
5
+ def publish(routing_key, msg, opts = {})
6
+ @mutex.synchronize do
7
+ ensure_connection! unless connected?
8
+ end
9
+ opts.merge! routing_key: routing_key
10
+ serialized_msg = serialize(msg)
11
+ @exchange.publish(serialized_msg, opts)
12
+ end
13
+
14
+ protected
15
+
16
+ def serialize(msg)
17
+ JSON.generate(msg)
18
+ end
19
+
20
+ private
21
+
22
+ def ensure_connection!
23
+ opts = { heartbeat: @opts[:heartbeat] }
24
+ opts.merge!(vhost: @opts[:vhost]) if @opts[:vhost]
25
+ @bunny = Bunny.new(@opts[:amqp_publish] || @opts[:amqp], opts)
26
+ @bunny.start
27
+ @channel = @bunny.create_channel
28
+ @exchange = @channel.exchange(
29
+ @opts[:exchange],
30
+ type: @opts[:exchange_type],
31
+ durable: @opts[:durable]
32
+ )
33
+ end
34
+
35
+ def connected?
36
+ @bunny && @bunny.connected?
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ module LaGear
2
+ class SneakersConfigurer
3
+ class << self
4
+ def configure_bi_amqp_endpoints(rx_uri_str, tx_uri_str)
5
+ parser = UriParser.new(rx_uri_str)
6
+ Sneakers.configure(
7
+ amqp: parser.amqp,
8
+ vhost: parser.vhost
9
+ )
10
+ parser = UriParser.new(tx_uri_str)
11
+ Sneakers.configure(
12
+ amqp_publish: parser.amqp,
13
+ vhost_publish: parser.vhost
14
+ )
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ module LaGear
2
+ class UriParser
3
+ def initialize(uri_str)
4
+ @uri = URI(uri_str)
5
+ end
6
+
7
+ def amqp
8
+ "#{@uri.scheme}://#{@uri.userinfo}@#{@uri.host}:#{@uri.port}"
9
+ end
10
+
11
+ def vhost
12
+ @uri.path.gsub!(/^\//, '')
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module LaGear
2
+ VERSION = '1.0.0-alpha.1'
3
+ end
@@ -0,0 +1,69 @@
1
+ module LaGear
2
+ module Worker
3
+ def self.included(base)
4
+ # for some reason, Rails is complaining about include being private so this
5
+ # include isn't working. Add include Sneakers::Worker to Rails workers for now...
6
+
7
+ if sidekiq_proc?
8
+ base.extend(DefaultClassMethods)
9
+ else
10
+ base.include(Sneakers::Worker) if base.respond_to?(:include)
11
+ base.extend(SneakersClassMethods)
12
+ Sneakers.logger.debug "#{}"
13
+ base.from_queue base.default_queue_name, base.default_queue_opts
14
+ end
15
+ base.include(::Sidekiq::Worker) if base.respond_to?(:include)
16
+ end
17
+
18
+ class << self
19
+ def sidekiq_proc?
20
+ $0.ends_with?('sidekiq')
21
+ end
22
+ end
23
+
24
+ module SneakersClassMethods
25
+ def default_queue_name
26
+ "#{Sneakers::Config.fetch(:app_name, 'sneakers_app').underscore}.#{routing_key}"
27
+ end
28
+
29
+ def default_queue_opts
30
+ {
31
+ routing_key: [routing_key, retry_routing_key],
32
+ arguments: default_queue_args,
33
+ handler_opts: {
34
+ routing_key: retry_routing_key
35
+ }
36
+ }
37
+ end
38
+
39
+ def routing_key
40
+ # TODO: the splitting is a semantic smell...
41
+ name.split('::').last.underscore
42
+ end
43
+
44
+ def retry_routing_key
45
+ "#{default_queue_name}.retry"
46
+ end
47
+
48
+ def default_queue_args
49
+ { 'x-dead-letter-exchange' => "#{Sneakers::Config.fetch(:exchange, 'sneakers').underscore}.retry" }
50
+ end
51
+ end
52
+
53
+ module DefaultClassMethods
54
+ def from_queue(_dummy_name, _dummy_opts); end
55
+
56
+ SneakersClassMethods.instance_methods(false).each { |m| define_method(m) { {} } }
57
+ end
58
+
59
+ def work(msg)
60
+ msg = deserialize(msg)
61
+ self.class.perform_async(*msg.values)
62
+ ack!
63
+ end
64
+
65
+ def deserialize(msg)
66
+ JSON.parse(msg)
67
+ end
68
+ end
69
+ end
data/lib/la_gear.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'la_gear/version'
2
+
3
+ require 'json'
4
+ require 'active_support/inflector'
5
+ require 'bunny'
6
+
7
+ require 'sneakers'
8
+ require 'sneakers/worker'
9
+ require 'la_gear/worker'
10
+ require 'la_gear/publisher'
11
+ require 'la_gear/bus'
12
+ require 'la_gear/uri_parser'
13
+ require 'la_gear/sneakers_configurer'
14
+
15
+ module LaGear
16
+ module Rails
17
+ if defined? ::Rails::Engine
18
+ require 'la_gear/engine'
19
+ end
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: la_gear
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre.alpha.1
5
+ platform: ruby
6
+ authors:
7
+ - Gabriel Chaney
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bunny
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.6.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.6.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: sneakers
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sidekiq
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.3'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: connection_pool
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.1'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '10.4'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '10.4'
111
+ description:
112
+ email:
113
+ - gabriel.chaney@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rubocop.yml"
120
+ - Gemfile
121
+ - LICENSE.txt
122
+ - README.md
123
+ - Rakefile
124
+ - la_gear.gemspec
125
+ - lib/la_gear.rb
126
+ - lib/la_gear/active_record.rb
127
+ - lib/la_gear/active_record/publish_triggerable.rb
128
+ - lib/la_gear/bus.rb
129
+ - lib/la_gear/engine.rb
130
+ - lib/la_gear/publisher.rb
131
+ - lib/la_gear/sneakers_configurer.rb
132
+ - lib/la_gear/uri_parser.rb
133
+ - lib/la_gear/version.rb
134
+ - lib/la_gear/worker.rb
135
+ homepage: https://github.com/giftcardzen/la_gear
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">"
151
+ - !ruby/object:Gem::Version
152
+ version: 1.3.1
153
+ requirements: []
154
+ rubyforge_project:
155
+ rubygems_version: 2.2.3
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: A thin abstraction on-top-of [sneakers](https://github.com/jondot/sneakers)
159
+ to DRY your workers. Pump it up!
160
+ test_files: []
161
+ has_rdoc: