devise-async-0.9.0/0000755000175600017570000000000012617611566013166 5ustar pravipravidevise-async-0.9.0/LICENSE0000644000175600017570000000206012617611566014171 0ustar pravipraviCopyright (c) 2012 Marcelo Silveira MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.devise-async-0.9.0/.travis.yml0000644000175600017570000000003612617611566015276 0ustar pravipravilanguage: ruby rvm: - 1.9.3 devise-async-0.9.0/devise-async.gemspec0000644000175600017570000000331112617611566017123 0ustar pravipravi# -*- encoding: utf-8 -*- require File.expand_path('../lib/devise/async/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["Marcelo Silveira"] gem.email = ["marcelo@mhfs.com.br"] gem.description = %q{Send Devise's emails in background. Supports Resque, Sidekiq, Delayed::Job and QueueClassic.} gem.summary = %q{Devise Async provides an easy way to configure Devise to send its emails asynchronously using your preferred queuing backend. It supports Resque, Sidekiq, Delayed::Job and QueueClassic.} gem.homepage = "https://github.com/mhfs/devise-async/" gem.files = `git ls-files`.split($\) gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.name = "devise-async" gem.require_paths = ["lib"] gem.version = Devise::Async::VERSION gem.add_dependency "devise", "~> 3.2" gem.add_development_dependency "activerecord", ">= 3.2" gem.add_development_dependency "actionpack", ">= 3.2" gem.add_development_dependency "actionmailer", ">= 3.2" gem.add_development_dependency "sqlite3", "~> 1.3" gem.add_development_dependency "resque", "~> 1.20" gem.add_development_dependency "sidekiq", "~> 1.2" gem.add_development_dependency "delayed_job_active_record", "~> 0.3" gem.add_development_dependency "queue_classic", "~> 2.0" gem.add_development_dependency "mocha", "~> 0.11" gem.add_development_dependency "minitest", "~> 3.0" gem.add_development_dependency "torquebox-no-op", "~> 2.3" end devise-async-0.9.0/README.md0000644000175600017570000000566612617611566014462 0ustar pravipravi# Devise Async [![Build Status](https://secure.travis-ci.org/mhfs/devise-async.png)](http://travis-ci.org/mhfs/devise-async) Devise Async provides an easy way to configure Devise to send its emails asynchronously using your preferred queuing backend. Supported backends: * Resque * Sidekiq * Delayed::Job * QueueClassic * Torquebox ## Installation Add this line to your application's Gemfile: ```ruby gem 'devise-async' ``` And then execute: $ bundle Or install it yourself as: $ gem install devise-async ## Usage Add `:async` to the `devise` call in your model: ```ruby class User < ActiveRecord::Base devise :database_authenticatable, :async, :confirmable # etc ... end ``` Set your queuing backend by creating `config/initializers/devise_async.rb`: ```ruby # Supported options: :resque, :sidekiq, :delayed_job, :queue_classic, :torquebox Devise::Async.backend = :resque ``` Tip: it defaults to Resque. You don't need to create the initializer if using it. ## Advanced Options ### Enabling via config The gem can be enabled/disabled easily via config, for example based on environment. ```ruby # config/initializers/devise_async.rb Devise::Async.enabled = true # | false ``` ### Custom mailer class Customize `Devise.mailer` at will and `devise-async` will honor it. Upgrade note: if you're upgrading from any version < 0.6 and getting errors trying to set `Devise::Async.mailer` just use `Devise.mailer` instead. ### Custom queue Let you specify a custom queue where to enqueue your background Devise jobs. Defaults to :mailer. ```ruby # config/initializers/devise_async.rb Devise::Async.queue = :my_custom_queue ``` ### Setup via block To avoid repeating `Devise::Async` in the initializer file you can use the block syntax similar do what `Devise` offers. ```ruby # config/initializers/devise_async.rb Devise::Async.setup do |config| config.enabled = true config.backend = :resque config.queue = :my_custom_queue end ``` ## Testing Be aware that since version 0.3.0 devise-async enqueues the background job in active record's `after_commit` hook. If you're using rspec's `use_transactional_fixtures` the jobs might not be enqueued as you'd expect. More details in this stackoverflow [thread](http://stackoverflow.com/questions/13406248/how-do-i-get-devise-async-working-with-cucumber/13465089#13465089). ## Devise < 2.2 Older versions of Devise are supported in the [devise_2_1](https://github.com/mhfs/devise-async/tree/devise_2_1) branch and in the 0.5 series of devise-async. Please refer to that branch README for further info. ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Added some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request ## License Released under the MIT License. See the [LICENSE][license] file for further details. [license]: https://github.com/mhfs/devise-async/blob/master/LICENSE devise-async-0.9.0/test/0000755000175600017570000000000012617611566014145 5ustar pravipravidevise-async-0.9.0/test/test_helper.rb0000644000175600017570000000074212617611566017013 0ustar pravipraviENV["RAILS_ENV"] = "test" require "minitest/autorun" require "minitest/spec" require "minitest/mock" require "mocha/setup" require "devise" require "devise/async" require "rails/all" require "resque" require "sidekiq" require "delayed_job_active_record" require "sidekiq/testing" require "torquebox-no-op" require "support/rails_app" require "support/test_helpers" require "support/my_mailer" include TestHelpers load File.dirname(__FILE__) + "/support/rails_app/db/schema.rb" devise-async-0.9.0/test/support/0000755000175600017570000000000012617611566015661 5ustar pravipravidevise-async-0.9.0/test/support/test_helpers.rb0000644000175600017570000000161212617611566020707 0ustar pravipravimodule TestHelpers def valid_attributes(attributes={}) { :username => "usertest", :email => generate_unique_email, :password => '12345678', :password_confirmation => '12345678' }.update(attributes) end email_count = 0 define_method :generate_unique_email do email_count += 1 "test#{email_count}@example.com" end def new_user(attributes={}) User.new(valid_attributes(attributes)) end def create_user(attributes={}) User.create!(valid_attributes(attributes)) end def new_user_with_mailer(attributes={}) UserWithMailer.new(valid_attributes(attributes)) end def create_user_with_mailer(attributes={}) UserWithMailer.create!(valid_attributes(attributes)) end def new_admin(attributes={}) Admin.new(valid_attributes(attributes)) end def create_admin(attributes={}) Admin.create!(valid_attributes(attributes)) end end devise-async-0.9.0/test/support/my_mailer.rb0000644000175600017570000000004412617611566020162 0ustar pravipraviclass MyMailer < Devise::Mailer end devise-async-0.9.0/test/support/rails_app.rb0000644000175600017570000000065612617611566020167 0ustar pravipravi# Silent schema load output ActiveRecord::Migration.verbose = false module Devise module Async class RailsApp < ::Rails::Application config.root = File.dirname(__FILE__) + "/rails_app" config.active_support.deprecation = :log config.action_mailer.default_url_options = { :host => "localhost:3000" } config.action_mailer.delivery_method = :test end end end Devise::Async::RailsApp.initialize! devise-async-0.9.0/test/support/rails_app/0000755000175600017570000000000012617611566017633 5ustar pravipravidevise-async-0.9.0/test/support/rails_app/db/0000755000175600017570000000000012617611566020220 5ustar pravipravidevise-async-0.9.0/test/support/rails_app/db/schema.rb0000644000175600017570000000600212617611566022003 0ustar pravipraviActiveRecord::Schema.define(:version => 1) do begin drop_table :users drop_rable :delayed_jobs rescue Exception => e end create_table "users", :force => true do |t| t.string "username" t.string "facebook_token" t.string "email", :default => "", :null => false t.string "unconfirmed_email", :default => "" t.string "encrypted_password", :limit => 128, :default => "", :null => false t.string "confirmation_token" t.datetime "confirmed_at" t.datetime "confirmation_sent_at" t.string "reset_password_token" t.datetime "remember_created_at" t.integer "sign_in_count", :default => 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" t.integer "failed_attempts", :default => 0 t.string "unlock_token" t.datetime "locked_at" t.string "authentication_token" t.datetime "created_at" t.datetime "updated_at" end create_table "admins", :force => true do |t| t.string "username" t.string "facebook_token" t.string "email", :default => "", :null => false t.string "unconfirmed_email", :default => "" t.string "encrypted_password", :limit => 128, :default => "", :null => false t.string "confirmation_token" t.datetime "confirmed_at" t.datetime "confirmation_sent_at" t.string "reset_password_token" t.datetime "remember_created_at" t.integer "sign_in_count", :default => 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" t.integer "failed_attempts", :default => 0 t.string "unlock_token" t.datetime "locked_at" t.string "authentication_token" t.datetime "created_at" t.datetime "updated_at" end create_table :delayed_jobs, :force => true do |table| table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually. table.text :handler # YAML-encoded string of the object that will do work table.text :last_error # reason for last failure (See Note below) table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. table.datetime :locked_at # Set when a client is working on this object table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) table.string :locked_by # Who is working on this object (if locked) table.string :queue # The name of the queue this job is in table.timestamps end end devise-async-0.9.0/test/support/rails_app/log/0000755000175600017570000000000012617611566020414 5ustar pravipravidevise-async-0.9.0/test/support/rails_app/log/development.log0000644000175600017570000000000012617611566023427 0ustar pravipravidevise-async-0.9.0/test/support/rails_app/config/0000755000175600017570000000000012617611566021100 5ustar pravipravidevise-async-0.9.0/test/support/rails_app/config/initializers/0000755000175600017570000000000012617611566023606 5ustar pravipravidevise-async-0.9.0/test/support/rails_app/config/initializers/devise.rb0000644000175600017570000002461012617611566025415 0ustar pravipravi# Use this hook to configure devise mailer, warden hooks and so forth. # Many of these configuration options can be set straight in your model. Devise.setup do |config| # ==> Mailer Configuration # Configure the e-mail address which will be shown in Devise::Mailer, # note that it will be overwritten if you use your own mailer class with default "from" parameter. config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" # Configure the class responsible to send e-mails. # config.mailer = "Devise::Mailer" # config.mailer = "Devise::Mailer" # ==> ORM configuration # Load and configure the ORM. Supports :active_record (default) and # :mongoid (bson_ext recommended) by default. Other ORMs may be # available as additional gems. require 'devise/orm/active_record' # ==> Configuration for any authentication mechanism # Configure which keys are used when authenticating a user. The default is # just :email. You can configure it to use [:username, :subdomain], so for # authenticating a user, both parameters are required. Remember that those # parameters are used only when authenticating and not when retrieving from # session. If you need permissions, you should implement that in a before filter. # You can also supply a hash where the value is a boolean determining whether # or not authentication should be aborted when the value is not present. # config.authentication_keys = [ :email ] # Configure parameters from the request object used for authentication. Each entry # given should be a request method and it will automatically be passed to the # find_for_authentication method and considered in your model lookup. For instance, # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. # The same considerations mentioned for authentication_keys also apply to request_keys. # config.request_keys = [] # Configure which authentication keys should be case-insensitive. # These keys will be downcased upon creating or modifying a user and when used # to authenticate or find a user. Default is :email. config.case_insensitive_keys = [ :email ] # Configure which authentication keys should have whitespace stripped. # These keys will have whitespace before and after removed upon creating or # modifying a user and when used to authenticate or find a user. Default is :email. config.strip_whitespace_keys = [ :email ] # Tell if authentication through request.params is enabled. True by default. # It can be set to an array that will enable params authentication only for the # given strategies, for example, `config.params_authenticatable = [:database]` will # enable it only for database (email + password) authentication. # config.params_authenticatable = true # Tell if authentication through HTTP Basic Auth is enabled. False by default. # It can be set to an array that will enable http authentication only for the # given strategies, for example, `config.http_authenticatable = [:token]` will # enable it only for token authentication. # config.http_authenticatable = false # If http headers should be returned for AJAX requests. True by default. # config.http_authenticatable_on_xhr = true # The realm used in Http Basic Authentication. "Application" by default. # config.http_authentication_realm = "Application" # It will change confirmation, password recovery and other workflows # to behave the same regardless if the e-mail provided was right or wrong. # Does not affect registerable. # config.paranoid = true # By default Devise will store the user in session. You can skip storage for # :http_auth and :token_auth by adding those symbols to the array below. # Notice that if you are skipping storage for all authentication paths, you # may want to disable generating routes to Devise's sessions controller by # passing :skip => :sessions to `devise_for` in your config/routes.rb config.skip_session_storage = [:http_auth] # ==> Configuration for :database_authenticatable # For bcrypt, this is the cost for hashing the password and defaults to 10. If # using other encryptors, it sets how many times you want the password re-encrypted. # # Limiting the stretches to just one in testing will increase the performance of # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use # a value less than 10 in other environments. config.stretches = Rails.env.test? ? 1 : 10 # Setup a pepper to generate the encrypted password. config.pepper = "e6b692498a53202d3be57ac3517afa2252d7b6eec044e8ff1e0f2f1119afc92d03305e6cb1715da01009c2ae8dbcdb0176612b2d1390bfadf55527a78f247f7b" # ==> Configuration for :confirmable # A period that the user is allowed to access the website even without # confirming his account. For instance, if set to 2.days, the user will be # able to access the website for two days without confirming his account, # access will be blocked just in the third day. Default is 0.days, meaning # the user cannot access the website without confirming his account. # config.allow_unconfirmed_access_for = 2.days # If true, requires any email changes to be confirmed (exactly the same way as # initial account confirmation) to be applied. Requires additional unconfirmed_email # db field (see migrations). Until confirmed new email is stored in # unconfirmed email column, and copied to email column on successful confirmation. config.reconfirmable = true # Defines which key will be used when confirming an account # config.confirmation_keys = [ :email ] # ==> Configuration for :rememberable # The time the user will be remembered without asking for credentials again. # config.remember_for = 2.weeks # If true, extends the user's remember period when remembered via cookie. # config.extend_remember_period = false # Options to be passed to the created cookie. For instance, you can set # :secure => true in order to force SSL only cookies. # config.rememberable_options = {} # ==> Configuration for :validatable # Range for password length. Default is 6..128. # config.password_length = 6..128 # Email regex used to validate email formats. It simply asserts that # an one (and only one) @ exists in the given string. This is mainly # to give user feedback and not to assert the e-mail validity. # config.email_regexp = /\A[^@]+@[^@]+\z/ # ==> Configuration for :timeoutable # The time you want to timeout the user session without activity. After this # time the user will be asked for credentials again. Default is 30 minutes. # config.timeout_in = 30.minutes # ==> Configuration for :lockable # Defines which strategy will be used to lock an account. # :failed_attempts = Locks an account after a number of failed attempts to sign in. # :none = No lock strategy. You should handle locking by yourself. # config.lock_strategy = :failed_attempts # Defines which key will be used when locking and unlocking an account # config.unlock_keys = [ :email ] # Defines which strategy will be used to unlock an account. # :email = Sends an unlock link to the user email # :time = Re-enables login after a certain amount of time (see :unlock_in below) # :both = Enables both strategies # :none = No unlock strategy. You should handle unlocking by yourself. # config.unlock_strategy = :both # Number of authentication tries before locking an account if lock_strategy # is failed attempts. # config.maximum_attempts = 20 # Time interval to unlock the account if :time is enabled as unlock_strategy. # config.unlock_in = 1.hour # ==> Configuration for :recoverable # # Defines which key will be used when recovering the password for an account # config.reset_password_keys = [ :email ] # Time interval you can reset your password with a reset password key. # Don't put a too small interval or your users won't have the time to # change their passwords. config.reset_password_within = 6.hours # ==> Configuration for :encryptable # Allow you to use another encryption algorithm besides bcrypt (default). You can use # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) # and :restful_authentication_sha1 (then you should set stretches to 10, and copy # REST_AUTH_SITE_KEY to pepper) # config.encryptor = :sha512 # ==> Configuration for :token_authenticatable # Defines name of the authentication token params key # config.token_authentication_key = :auth_token # ==> Scopes configuration # Turn scoped views on. Before rendering "sessions/new", it will first check for # "users/sessions/new". It's turned off by default because it's slower if you # are using only default views. # config.scoped_views = false # Configure the default scope given to Warden. By default it's the first # devise role declared in your routes (usually :user). # config.default_scope = :user # Configure sign_out behavior. # Sign_out action can be scoped (i.e. /users/sign_out affects only :user scope). # The default is true, which means any logout action will sign out all active scopes. # config.sign_out_all_scopes = true # ==> Navigation configuration # Lists the formats that should be treated as navigational. Formats like # :html, should redirect to the sign in page when the user does not have # access, but formats like :xml or :json, should return 401. # # If you have any extra navigational formats, like :iphone or :mobile, you # should add them to the navigational formats lists. # # The "*/*" below is required to match Internet Explorer requests. # config.navigational_formats = ["*/*", :html] # The default HTTP method used to sign out a resource. Default is :delete. config.sign_out_via = :delete # ==> OmniAuth # Add a new OmniAuth provider. Check the wiki for more information on setting # up on your models and hooks. # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo' # ==> Warden configuration # If you want to use other strategies, that are not supported by Devise, or # change the failure app, you can configure them inside the config.warden block. # # config.warden do |manager| # manager.intercept_401 = false # manager.default_strategies(:scope => :user).unshift :some_external_strategy # end # config.secret_key = 'abc' if config.respond_to? :secret_key # required for devise 3.1 end devise-async-0.9.0/test/support/rails_app/config/initializers/devise_async.rb0000644000175600017570000000010312617611566026601 0ustar pravipraviDevise::Async.setup do |config| config.queue = :custom_queue end devise-async-0.9.0/test/support/rails_app/config/database.yml0000644000175600017570000000012612617611566023366 0ustar pravipravitest: adapter: sqlite3 database: db/development.sqlite3 pool: 5 timeout: 5000 devise-async-0.9.0/test/support/rails_app/config/routes.rb0000644000175600017570000000007112617611566022744 0ustar pravipraviRails.application.routes.draw do devise_for :users end devise-async-0.9.0/test/support/rails_app/app/0000755000175600017570000000000012617611566020413 5ustar pravipravidevise-async-0.9.0/test/support/rails_app/app/models/0000755000175600017570000000000012617611566021676 5ustar pravipravidevise-async-0.9.0/test/support/rails_app/app/models/user_with_mailer.rb0000644000175600017570000000011012617611566025555 0ustar pravipraviclass UserWithMailer < User def devise_mailer MyMailer end end devise-async-0.9.0/test/support/rails_app/app/models/user.rb0000644000175600017570000000027412617611566023204 0ustar pravipraviclass User < ActiveRecord::Base devise :database_authenticatable, :confirmable, :lockable, :recoverable, :registerable, :rememberable, :timeoutable, :trackable, :validatable end devise-async-0.9.0/test/support/rails_app/app/models/admin.rb0000644000175600017570000000030512617611566023311 0ustar pravipraviclass Admin < ActiveRecord::Base devise :database_authenticatable, :confirmable, :lockable, :recoverable, :registerable, :rememberable, :timeoutable, :trackable, :validatable, :async end devise-async-0.9.0/test/devise/0000755000175600017570000000000012617611566015424 5ustar pravipravidevise-async-0.9.0/test/devise/async_test.rb0000644000175600017570000000070712617611566020131 0ustar pravipravirequire "test_helper" module Devise describe "Async" do it "yields self when setup is called" do Async.setup { |config| config.must_equal Async } end it "stores backend config" do Async.backend = :something Async.backend.must_equal :something end it "stores enabled config" do Async.backend = false Async.backend.must_equal false end after do Async.backend = :resque end end end devise-async-0.9.0/test/devise/async/0000755000175600017570000000000012617611566016541 5ustar pravipravidevise-async-0.9.0/test/devise/async/model_test.rb0000644000175600017570000000542012617611566021226 0ustar pravipravirequire "test_helper" module Devise module Async describe "Model" do it "accumulates notifications to be sent after commit on Model creation" do Admin.transaction do admin = create_admin mailers = admin.send(:devise_pending_notifications) # [:confirmation_instructions, ["RUQUib67wLcCiEyZMwfx", {}]] mailers.size.must_equal 1 mailer = mailers.first mailer.size.must_equal 2 mailer.first.must_equal :confirmation_instructions mailer.last.must_be_instance_of Array end end it "immediately sends notifications when the model has not changed" do admin = create_admin Worker.expects(:enqueue).with(:confirmation_instructions, "Admin", admin.id.to_s, instance_of(String), {}) admin.send_confirmation_instructions end it "accumulates notifications to be sent after commit when Model has been changed" do admin = create_admin Admin.transaction do admin[:username] = "changed_username" admin.send_confirmation_instructions mailers = admin.send(:devise_pending_notifications) # [:confirmation_instructions, ["RUQUib67wLcCiEyZMwfx", {}]] mailers.size.must_equal 1 mailer = mailers.first mailer.size.must_equal 2 mailer.first.must_equal :confirmation_instructions mailer.last.must_be_instance_of Array Worker.expects(:enqueue).never # after_commit will not fire without save end end it "triggers the enqueued notifications on save" do admin = create_admin Admin.transaction do admin[:username] = "changed_username" admin.send_confirmation_instructions mailers = admin.send(:devise_pending_notifications) # [:confirmation_instructions, ["RUQUib67wLcCiEyZMwfx", {}]] mailers.size.must_equal 1 mailer = mailers.first mailer.size.must_equal 2 mailer.first.must_equal :confirmation_instructions mailer.last.must_be_instance_of Array admin.save Worker.expects(:enqueue).with(:confirmation_instructions, "Admin", admin.id.to_s, instance_of(String), {}) end end it "should not enqueue a job if the enabled config option is set to false" do Devise::Async.stubs(:enabled).returns(false) # Stubbing the devise's confirmation_instructions confirmation_email = Object.new Devise::Mailer.stubs(:confirmation_instructions).returns(confirmation_email) confirmation_email.stubs(:deliver).returns(true) # Stubbing the email sending process admin = create_admin admin.send(:devise_pending_notifications).must_equal [] Worker.expects(:enqueue).never end end end end devise-async-0.9.0/test/devise/async/backend_test.rb0000644000175600017570000000132112617611566021511 0ustar pravipravirequire "test_helper" module Devise module Async describe "Backend" do it "gives resque as the backend" do Backend.for(:resque).must_equal Backend::Resque end it "gives sidekiq as the backend" do Backend.for(:sidekiq).must_equal Backend::Sidekiq end it "gives delayed job as the backend" do Backend.for(:delayed_job).must_equal Backend::DelayedJob end it "gives queue classic as the backend" do Backend.for(:queue_classic).must_equal Backend::QueueClassic end it "alerts about unsupported backend" do assert_raises ArgumentError do Backend.for(:unsupported_backend) end end end end end devise-async-0.9.0/test/devise/async/backend/0000755000175600017570000000000012617611566020130 5ustar pravipravidevise-async-0.9.0/test/devise/async/backend/base_test.rb0000644000175600017570000000154212617611566022430 0ustar pravipravirequire "test_helper" module Devise module Async module Backend describe "Base" do it "delegates to configured mailer" do Devise.mailer = "MyMailer" user = create_user mailer_instance = mock(:deliver => true) MyMailer.expects(:confirmation_instructions).once.returns(mailer_instance) Base.new.perform(:confirmation_instructions, "User", user.id, {}) end it "delegates to model configured mailer" do user = create_user_with_mailer mailer_instance = mock(:deliver => true) MyMailer.expects(:confirmation_instructions).once.returns(mailer_instance) Base.new.perform(:confirmation_instructions, "UserWithMailer", user.id, {}) end after do Devise.mailer = "Devise::Mailer" end end end end end devise-async-0.9.0/test/devise/async/backend/torquebox_test.rb0000644000175600017570000000114612617611566023546 0ustar pravipravirequire "test_helper" module Devise module Async module Backend describe "Torquebox" do it "enqueues job" do Torquebox.any_instance.expects(:perform).with(:mailer_method, "User", 123, {}) Torquebox.enqueue(:mailer_method, "User", 123, {}) end it "delegates to devise mailer when delivering" do user = create_user ActionMailer::Base.deliveries = [] Backend::Torquebox.new.perform(:confirmation_instructions, "User", user.id, {}) ActionMailer::Base.deliveries.size.must_equal 1 end end end end end devise-async-0.9.0/test/devise/async/backend/queue_classic_test.rb0000644000175600017570000000165112617611566024344 0ustar pravipravirequire "test_helper" module Devise module Async module Backend describe "QueueClassic" do it "enqueues job" do ::QC::Queue.any_instance.expects(:enqueue).with( "Devise::Async::Backend::QueueClassic.perform", "mailer_method", "User", 123, {}) QueueClassic.enqueue(:mailer_method, "User", 123, {}) end it "delegates to devise mailer when delivering" do user = create_user ActionMailer::Base.deliveries = [] Backend::QueueClassic.perform(:confirmation_instructions, "User", user.id, {}) ActionMailer::Base.deliveries.size.must_equal 1 end it "enqueues to configured queue" do queue = mock(:enqueue => nil) ::QC::Queue.expects(:new).with(:custom_queue).once.returns(queue) QueueClassic.enqueue(:mailer_method, "User", 123, {}) end end end end end devise-async-0.9.0/test/devise/async/backend/resque_test.rb0000644000175600017570000000150212617611566023016 0ustar pravipravirequire "test_helper" module Devise module Async module Backend describe "Resque" do it "enqueues job" do ::Resque.expects(:enqueue).with(Resque, :mailer_method, "User", 123, {}) Resque.enqueue(:mailer_method, "User", 123, {}) end it "delegates to devise mailer when delivering" do user = create_user ActionMailer::Base.deliveries = [] Backend::Resque.perform(:confirmation_instructions, "User", user.id, {}) ActionMailer::Base.deliveries.size.must_equal 1 end it "enqueues to configured queue" do expected_size = 1 + ::Resque.size(:custom_queue) Resque.enqueue(:mailer_method, "User", 123, {}) ::Resque.size(:custom_queue).must_equal expected_size end end end end end devise-async-0.9.0/test/devise/async/backend/delayed_job_test.rb0000644000175600017570000000133612617611566023760 0ustar pravipravirequire "test_helper" module Devise module Async module Backend describe "DelayedJob" do it "enqueues job" do delayed_instance = mock() delayed_instance.expects(:perform).once.with(:mailer_method, "User", 123, {}) DelayedJob.any_instance.expects(:delay).once.returns(delayed_instance) DelayedJob.enqueue(:mailer_method, "User", 123, {}) end it "delegates to devise mailer when delivering" do user = create_user ActionMailer::Base.deliveries = [] Backend::DelayedJob.new.perform(:confirmation_instructions, "User", user.id, {}) ActionMailer::Base.deliveries.size.must_equal 1 end end end end end devise-async-0.9.0/test/devise/async/backend/sidekiq_test.rb0000644000175600017570000000146112617611566023147 0ustar pravipravirequire "test_helper" module Devise module Async module Backend describe "Sidekiq" do it "enqueues job" do Sidekiq.expects(:perform_async).with(:mailer_method, "User", 123, {}) Sidekiq.enqueue(:mailer_method, "User", 123, {}) end it "delegates to devise mailer when delivering" do user = create_user ActionMailer::Base.deliveries = [] Backend::Sidekiq.new.perform(:confirmation_instructions, "User", user.id, {}) ActionMailer::Base.deliveries.size.must_equal 1 end it "enqueues to configured queue" do expected_size = 1 + Sidekiq.jobs.size Sidekiq.enqueue(:mailer_method, "User", 123, {}) Sidekiq.jobs.size.must_equal expected_size end end end end end devise-async-0.9.0/test/devise/async/worker_test.rb0000644000175600017570000000210312617611566021432 0ustar pravipravirequire "test_helper" module Devise module Async describe "Worker" do it "enqueues job using the resque backend" do Devise::Async.backend = :resque Backend::Resque.expects(:enqueue).with(:mailer_method, "User", 123, {}) Worker.enqueue(:mailer_method, "User", 123, {}) end it "enqueues job using the sidekiq backend" do Devise::Async.backend = :sidekiq Backend::Sidekiq.expects(:enqueue).with(:mailer_method, "User", 123, {}) Worker.enqueue(:mailer_method, "User", 123, {}) end it "enqueues job using the delayed job backend" do Devise::Async.backend = :delayed_job Backend::DelayedJob.expects(:enqueue).with(:mailer_method, "User", 123, {}) Worker.enqueue(:mailer_method, "User", 123, {}) end it "enqueues job using the queue classic backend" do Devise::Async.backend = :queue_classic Backend::QueueClassic.expects(:enqueue).with(:mailer_method, "User", 123, {}) Worker.enqueue(:mailer_method, "User", 123, {}) end end end end devise-async-0.9.0/CHANGELOG.md0000644000175600017570000000373312617611566015005 0ustar pravipravi## Unreleased ## 0.9.0 * Multiple mailers support (baschtl) * Update devise dependency to ~3.2 ## 0.8.0 * Support arbitrary number of arguments to mailer (glebm) * Added torquebox support (astjohn) ## 0.7.0 * make sure options hash has string keys when enqueued and symbol keys when job runs * stringfy options keys before enqueueing to make queue_classic happy (nickw) * Added `Devise::Async.enabled=` options to make it easier to skip async processing during tests (mohamedmagdy) ## 0.6.0 * Now compatible with Devise 2.2+ only * Legacy `Devise::Async::Proxy` is now gone * `Devise::Async.mailer=` is gone since there's no need for it anymore. Use `Devise.mailer` directly. ## 0.5.1 * Lock dependency of 0.5 series on devise < 2.2 ## 0.5.0 * Added support for QueueClassic (jperville) * Remove deprecated `DeviseAsync::Proxy` and `DeviseAsync.backend=` * Improved comments throughout code ## 0.4.0 * Enhancements * Add support for queue config to DelayedJob backend * Use Devise third party modules API insted of including module directly. This fixes the ordering issue when including. ## 0.3.1 * Fixes * Do not register after_commit unless ORM supports it * Only enqueue notifications for after_commit if model is dirty ## 0.3.0 * Fixes * Added `Devise::Async::Model` to use new devise's after_commit hooks to resolve #6 (only devise >=2.1.1) ## 0.2.0 * Enhancements * Added `Devise::Async.queue` option to let configure the queue the jobs will be enqueued to. ## 0.1.1 * Fixes * Changed the way we store the record id in the queue to enforce compatibility with diferent ORMs ## 0.1.0 * New * Added `Devise::Async.mailer` option to proxy to custom mailers * Added `Devise::Async.setup` to allow configuring with blocks ## 0.0.2 * Enhancements * Restructured gem to Devise::Async module instead of DeviseAsync. * Deprecations * DeviseAsync::Proxy is now Devise::Async::Proxy * DeviseAsync.backend is now Devise::Async.backend ## 0.0.1 * first release devise-async-0.9.0/Gemfile0000644000175600017570000000014112617611566014455 0ustar pravipravisource 'https://rubygems.org' # Specify your gem's dependencies in devise-async.gemspec gemspec devise-async-0.9.0/metadata.yml0000644000175600017570000001734412617611566015502 0ustar pravipravi--- !ruby/object:Gem::Specification name: devise-async version: !ruby/object:Gem::Version version: 0.9.0 platform: ruby authors: - Marcelo Silveira autorequire: bindir: bin cert_chain: [] date: 2013-11-19 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: devise requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '3.2' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '3.2' - !ruby/object:Gem::Dependency name: activerecord requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '3.2' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '3.2' - !ruby/object:Gem::Dependency name: actionpack requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '3.2' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '3.2' - !ruby/object:Gem::Dependency name: actionmailer requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '3.2' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '3.2' - !ruby/object:Gem::Dependency name: sqlite3 requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.3' - !ruby/object:Gem::Dependency name: resque requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.20' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.20' - !ruby/object:Gem::Dependency name: sidekiq requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.2' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.2' - !ruby/object:Gem::Dependency name: delayed_job_active_record requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '0.3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '0.3' - !ruby/object:Gem::Dependency name: queue_classic requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '2.0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '2.0' - !ruby/object:Gem::Dependency name: mocha requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '0.11' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '0.11' - !ruby/object:Gem::Dependency name: minitest requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '3.0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '3.0' - !ruby/object:Gem::Dependency name: torquebox-no-op requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '2.3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '2.3' description: Send Devise's emails in background. Supports Resque, Sidekiq, Delayed::Job and QueueClassic. email: - marcelo@mhfs.com.br executables: [] extensions: [] extra_rdoc_files: [] files: - .gitignore - .travis.yml - CHANGELOG.md - Gemfile - LICENSE - README.md - Rakefile - devise-async.gemspec - lib/devise-async.rb - lib/devise/async.rb - lib/devise/async/backend.rb - lib/devise/async/backend/base.rb - lib/devise/async/backend/delayed_job.rb - lib/devise/async/backend/queue_classic.rb - lib/devise/async/backend/resque.rb - lib/devise/async/backend/sidekiq.rb - lib/devise/async/backend/torquebox.rb - lib/devise/async/model.rb - lib/devise/async/version.rb - lib/devise/async/worker.rb - test/devise/async/backend/base_test.rb - test/devise/async/backend/delayed_job_test.rb - test/devise/async/backend/queue_classic_test.rb - test/devise/async/backend/resque_test.rb - test/devise/async/backend/sidekiq_test.rb - test/devise/async/backend/torquebox_test.rb - test/devise/async/backend_test.rb - test/devise/async/model_test.rb - test/devise/async/worker_test.rb - test/devise/async_test.rb - test/support/my_mailer.rb - test/support/rails_app.rb - test/support/rails_app/app/models/admin.rb - test/support/rails_app/app/models/user.rb - test/support/rails_app/app/models/user_with_mailer.rb - test/support/rails_app/config/database.yml - test/support/rails_app/config/initializers/devise.rb - test/support/rails_app/config/initializers/devise_async.rb - test/support/rails_app/config/routes.rb - test/support/rails_app/db/schema.rb - test/support/rails_app/log/development.log - test/support/test_helpers.rb - test/test_helper.rb homepage: https://github.com/mhfs/devise-async/ licenses: [] metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.0.3 signing_key: specification_version: 4 summary: Devise Async provides an easy way to configure Devise to send its emails asynchronously using your preferred queuing backend. It supports Resque, Sidekiq, Delayed::Job and QueueClassic. test_files: - test/devise/async/backend/base_test.rb - test/devise/async/backend/delayed_job_test.rb - test/devise/async/backend/queue_classic_test.rb - test/devise/async/backend/resque_test.rb - test/devise/async/backend/sidekiq_test.rb - test/devise/async/backend/torquebox_test.rb - test/devise/async/backend_test.rb - test/devise/async/model_test.rb - test/devise/async/worker_test.rb - test/devise/async_test.rb - test/support/my_mailer.rb - test/support/rails_app.rb - test/support/rails_app/app/models/admin.rb - test/support/rails_app/app/models/user.rb - test/support/rails_app/app/models/user_with_mailer.rb - test/support/rails_app/config/database.yml - test/support/rails_app/config/initializers/devise.rb - test/support/rails_app/config/initializers/devise_async.rb - test/support/rails_app/config/routes.rb - test/support/rails_app/db/schema.rb - test/support/rails_app/log/development.log - test/support/test_helpers.rb - test/test_helper.rb devise-async-0.9.0/lib/0000755000175600017570000000000012617611566013734 5ustar pravipravidevise-async-0.9.0/lib/devise/0000755000175600017570000000000012617611566015213 5ustar pravipravidevise-async-0.9.0/lib/devise/async.rb0000644000175600017570000000260312617611566016656 0ustar pravipravirequire "active_support/dependencies" require "devise/async/version" module Devise module Async autoload :Worker, "devise/async/worker" autoload :Backend, "devise/async/backend" autoload :Model, "devise/async/model" module Backend autoload :Base, "devise/async/backend/base" autoload :Resque, "devise/async/backend/resque" autoload :Sidekiq, "devise/async/backend/sidekiq" autoload :DelayedJob, "devise/async/backend/delayed_job" autoload :QueueClassic, "devise/async/backend/queue_classic" autoload :Torquebox, "devise/async/backend/torquebox" end # Defines the queue backend to be used. Resque by default. mattr_accessor :backend @@backend = :resque # Defines the queue in which the background job will be enqueued. Default is :mailer. mattr_accessor :queue @@queue = :mailer # Defines the enabled configuration that if set to false the emails will be sent synchronously mattr_accessor :enabled @@enabled = true # Allow configuring Devise::Async with a block # # Example: # # Devise::Async.setup do |config| # config.backend = :resque # config.queue = :my_custom_queue # end def self.setup yield self end end end # Register devise-async model in Devise Devise.add_module(:async, :model => 'devise/async/model') devise-async-0.9.0/lib/devise/async/0000755000175600017570000000000012617611566016330 5ustar pravipravidevise-async-0.9.0/lib/devise/async/model.rb0000644000175600017570000000376412617611566017767 0ustar pravipravimodule Devise module Models module Async extend ActiveSupport::Concern included do # Register hook to send all devise pending notifications. # # When supported by the ORM/database we send just after commit to # prevent the backend of trying to fetch the record and send the # notification before the record is committed to the databse. # # Otherwise we use after_save. if respond_to?(:after_commit) # AR only after_commit :send_devise_pending_notifications else # mongoid after_save :send_devise_pending_notifications end end protected # This method overwrites devise's own `send_devise_notification` # to capture all email notifications and enqueue it for background # processing instead of sending it inline as devise does by # default. def send_devise_notification(notification, *args) return super unless Devise::Async.enabled # If the record is dirty we keep pending notifications to be enqueued # by the callback and avoid before commit job processing. if changed? devise_pending_notifications << [ notification, args ] # If the record isn't dirty (aka has already been saved) enqueue right away # because the callback has already been triggered. else Devise::Async::Worker.enqueue(notification, self.class.name, self.id.to_s, *args) end end # Send all pending notifications. def send_devise_pending_notifications devise_pending_notifications.each do |notification, args| # Use `id.to_s` to avoid problems with mongoid 2.4.X ids being serialized # wrong with YAJL. Devise::Async::Worker.enqueue(notification, self.class.name, self.id.to_s, *args) end @devise_pending_notifications = [] end def devise_pending_notifications @devise_pending_notifications ||= [] end end end end devise-async-0.9.0/lib/devise/async/version.rb0000644000175600017570000000007512617611566020344 0ustar pravipravimodule Devise module Async VERSION = "0.9.0" end end devise-async-0.9.0/lib/devise/async/backend.rb0000644000175600017570000000050012617611566020237 0ustar pravipravimodule Devise module Async module Backend # Gives the desired backend driver class to be used to enqueue # jobs. def self.for(backend) const_get(backend.to_s.camelize) rescue NameError raise ArgumentError, "unsupported backend for devise-async." end end end end devise-async-0.9.0/lib/devise/async/backend/0000755000175600017570000000000012617611566017717 5ustar pravipravidevise-async-0.9.0/lib/devise/async/backend/torquebox.rb0000644000175600017570000000044712617611566022301 0ustar pravipravimodule Devise module Async module Backend class Torquebox < Base include ::TorqueBox::Messaging::Backgroundable always_background :perform def self.enqueue(*args) new.perform(*args) # using always_background end end end end end devise-async-0.9.0/lib/devise/async/backend/delayed_job.rb0000644000175600017570000000033012617611566022501 0ustar pravipravimodule Devise module Async module Backend class DelayedJob < Base def self.enqueue(*args) new.delay(:queue => Devise::Async.queue).perform(*args) end end end end end devise-async-0.9.0/lib/devise/async/backend/resque.rb0000644000175600017570000000050112617611566021544 0ustar pravipravimodule Devise module Async module Backend class Resque < Base @queue = Devise::Async.queue def self.enqueue(*args) args.unshift(self) ::Resque.enqueue(*args) end def self.perform(*args) new.perform(*args) end end end end end devise-async-0.9.0/lib/devise/async/backend/base.rb0000644000175600017570000000155112617611566021160 0ustar pravipravimodule Devise module Async module Backend class Base def self.enqueue(*args) raise NotImplementedError, "Any DeviseAssync::Backend subclass should implement `self.enqueue`." end # Loads the resource record and sends the email. # # It uses `orm_adapter` API to fetch the record in order to enforce # compatibility among diferent ORMs. def perform(method, resource_class, resource_id, *args) resource = resource_class.constantize.to_adapter.get!(resource_id) args[-1] = args.last.symbolize_keys if args.last.is_a?(Hash) mailer_class(resource).send(method, resource, *args).deliver end private def mailer_class(resource = nil) @mailer_class ||= resource.try(:devise_mailer) || Devise.mailer end end end end end devise-async-0.9.0/lib/devise/async/backend/sidekiq.rb0000644000175600017570000000041412617611566021674 0ustar pravipravimodule Devise module Async module Backend class Sidekiq < Base include ::Sidekiq::Worker sidekiq_options :queue => Devise::Async.queue def self.enqueue(*args) perform_async(*args) end end end end end devise-async-0.9.0/lib/devise/async/backend/queue_classic.rb0000644000175600017570000000073312617611566023074 0ustar pravipravirequire "queue_classic" module Devise module Async module Backend class QueueClassic < Base def self.enqueue(method, *args) queue = ::QC::Queue.new(Devise::Async.queue) method = String(method) # QC won't serialize Symbol such as #{method} args.unshift("#{self}.perform", method) queue.enqueue(*args) end def self.perform(*args) new.perform(*args) end end end end end devise-async-0.9.0/lib/devise/async/worker.rb0000644000175600017570000000145412617611566020172 0ustar pravipravimodule Devise module Async class Worker class << self # Used is the internal interface for devise-async to enqueue notifications # to the desired backend. def enqueue(method, resource_class, resource_id, *args) # convert args to strings and hashes with string keys before passing to backend args = stringify_args args backend_class.enqueue(method, resource_class, resource_id, *args) end private def stringify_args(args) args.map do |a| case a when Hash a.stringify_keys when Symbol a.to_s else a end end end def backend_class Backend.for(Devise::Async.backend) end end end end end devise-async-0.9.0/lib/devise-async.rb0000644000175600017570000000002712617611566016652 0ustar pravipravirequire "devise/async" devise-async-0.9.0/Rakefile0000644000175600017570000000034712617611566014637 0ustar pravipravi#!/usr/bin/env rake require "bundler/gem_tasks" require "rake/testtask" task :default => :test Rake::TestTask.new do |t| t.libs << "lib" t.libs << "test" t.test_files = FileList["test/**/*_test.rb"] t.verbose = true end devise-async-0.9.0/.gitignore0000644000175600017570000000035112617611566015155 0ustar pravipravi*.gem *.rbc .bundle .config .ruby-version .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp test/support/rails_app/log/* test/support/rails_app/db/*.sqlite3