I created a job like so:
class SendEmailJob < ActiveJob::Base
queue_as :default
def perform(user)
#user = user
UserMailer.welcome_email(#user).deliver_later
end
end
That uses my mailer:
class UserMailer < ActionMailer::Base
def welcome_email(user)
#user = user
mg_client = Mailgun::Client.new ENV['api_key']
message_params = {
:from => ENV["gmail_username"],
:to => #user.email,
:subject => "Welcome",
:text => "This is a welcome email"
}
mg_client.send_message ENV["domain"], message_params
end
end
My controller:
SendEmailJob.set(wait: 20.seconds).perform_later(#user)
I keep getting the following error:
NoMethodError (undefined method `set' for SendEmailJob:Class):
EDIT config/application.rb
require File.expand_path('../boot', FILE)
require 'rails/all'
require 'active_job'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module LinkbuilderPro
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
end
end
Rails 4.1.8
ActiveJob was integrated with Rails from version 4.2
Before that, you needed to use the active_job gem. As you are using Rails version 4.1.8, you have to use the active_job gem along with the old syntaxes. .set method was not available before Rails 4.2, so you are getting that error.
However, the syntax for Rails version 4.1 is:
YourJob.enqueue(record)
YourJob.enqueue(record, options)
So, in your case, it would be something like:
SendEmailJob.enqueue(#user, wait: 20.seconds)
perform_later is introduced in Rails 4.2
See this article for the active job differences between Rails 4.1 and 4.2
Try this in your controller:
SendEmailJob.new(#user).enqueue(wait: 20.seconds)
Related
I'm having trouble getting my session on Rails 7 to persist. I'm not even redirecting at any point, just staying on the one page.
application_controller.rb
class ApplicationController < ActionController::API
include ActionController::Cookies
end
application.rb
require_relative "boot"
require "rails/all"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module CsmBack
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
# Must add these lines!
# Adding back cookies and session middleware
config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore, key: '_namespace_key'
# Use SameSite=Strict for all cookies to help protect against CSRF
config.action_dispatch.cookies_same_site_protection = :strict
end
end
sessions_controller.rb
def login
if session[:user_id]
return render json: {errors: "Username or Password Wrong"}
end
user=User.find_by(username: params[:username])
if user&.authenticate(params[:password])
session[:user_id] = user.id
render json: user
else
render json: {errors: "Username or Password Wrong"}
end
end
If that last one worked, I should be able to click the button to trigger it twice in a row and get different values, but I'm not even getting that.
You seem to be throwing an error to the client side if they try to log in without logging out first.
According to what I see here, your server-side code, (however off it might seem) is fine. You might want to look at your client side, maybe set up a proxy
I am getting an error while trying to load a class from module which is present in app/lib folder.
I had made changes according to it
Controller
class SchemasController < ApplicationController
# include Schemas
# require './app/lib/api'
include Operator
end
class under module which I have to include:
# frozen_string_literal: true
module A
module B
class C
#url = "https://something.com"
def self.where(params = {})
response = A::Connection.connect(#url).get('headers', params)
response.body
end
end
end
end
application.rb:
require_relative "boot"
# Pick the frameworks you want:
require 'action_controller/railtie'
require 'active_job/railtie'
require 'action_cable/engine'
require 'action_mailer/railtie'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module AnalyticsQueryBuilder
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0
config.autoload_paths += %W(#{config.root}/lib)
# config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.before_configuration do
env_file = File.join(Rails.root, 'config', 'local_env.yml')
if File.exist?(env_file)
YAML.safe_load(File.open(env_file)).each do |key, value|
ENV[key.to_s] = value
end
end
end
Rails.logger = Logger.new($stdout)
config.exceptions_app = routes
end
end
How to include it in Controller
Have you tried doing something like include Api::AnalyticsQueryBuilderMetadataService::Operator ?
I've a Rails 5 API with devise/doorkeeper perfectly working.
I've built an engine having a front-end part with all devise views. Everything works unless the flash when warden is throwing an error. I've imported manually missing modules :
# app/controllers/concerns/api_to_web_controller.rb
module ApiToWebController
extend ActiveSupport::Concern
included do
include ActionController::Helpers
include ActionController::MimeResponds
include ActionController::Redirecting
include ActionView::Layouts
include ActionController::EtagWithFlash
include ActionController::Flash
respond_to :html
end
end
# app/controllers/concerns/devise_api_to_web.rb
##
# Module to integrate Devise:
# - helpers
# - layout
# It also redefine `redirect_to`.
# See https://github.com/heartcombo/responders/issues/222
# for more details
module DeviseApiToWeb
extend ActiveSupport::Concern
included do
layout 'secret_migration/devise'
if respond_to?(:helper_method)
helpers = %w[resource scope_name resource_name signed_in_resource
resource_class resource_params devise_mapping]
helper_method(*helpers)
end
def redirect_to(options = {}, response_options = {})
super
end
end
end
Here is my custom SessionController
module MyEngine
module V1
# Override Devise::SessionsController
class Users::SessionsController < Devise::SessionsController
include ::ApiToWebController
include ::DeviseApiToWeb
end
end
end
I got this custom after_authentication
# config/initializers/warden.rb
Warden::Manager.after_authentication do |user, auth, _opts|
next if user.is_ok?
throw(:warden, :message => "User not ok, contact admin")
end
Finally, the application file
module MyApp
class Application <
opts = { key: '_my_app', domain: 'example.com', tld_length: 1 }
Rails::ApplicationRails.application.config.session_store :disabled
config.session_store :cookie_store, opts
config.middleware.use ActionDispatch::Session::CookieStore, config.session_options
config.middleware.insert_after(ActionDispatch::Cookies, ActionDispatch::Session::CookieStore,
opts)
I've been able to track the data. env[warden.options] is well filled with :message => "User not ok, contact admin" but somehow, it's not passed after the redirection.
Important point, I got flash message when it's a pure devise error like Invalid Email or password..
I got it. It was a middleware order issue. It has to be with the following order
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use Warden::Manager
I've update my engine.rb to include the middleware at the correct place
module MyEngine
##
# Engine initializers
class Engine < ::Rails::Engine
isolate_namespace MyEngine
initializer 'use action dispatch flash' do |app|
app.config.middleware.insert_after(ActionDispatch::Session::CookieStore, ActionDispatch::Flash)
app.config.middleware.use Rack::MethodOverride
end
end
end
# config/application.rb
module MyApp
class Application < Rails::Application
# ...
config.middleware.insert_before(Warden::Manager, ActionDispatch::Cookies)
# ...
end
end
# config/environments/development.rb
Rails.application.configure do
opts = { key: '_my_app', domain: 'lvh.me', tld_length: 2 }
Rails.application.config.session_store :disabled
config.session_store :cookie_store, opts
config.middleware.insert_after(ActionDispatch::Cookies, ActionDispatch::Session::CookieStore,
opts)
end
I'm trying to follow this blog post to allow remote authentication with devise, but I can't figure out a few things.
What do I call my new files, and where do I put them?
The first one, Devise::Models::RemoteAuthenticatable I assume I call remote_authenticatable.rb and put in a devise folder in my models folder?
The second file, "Warden strategy for Devise" I have no idea what to call it, or where to put it.
Any ideas? I found the tutorial pretty incomplete. It's linked to from the Devise instructions as the way to do this.
EDIT:
I've been doing more reading, and I'm not sure I need to do what it says in that blog post. I've been trying to simply PUT data to my Rails app, but can't get anything to work. Doing a PUT request causes devise to loose authentication.
I know this question is years old, but I also found the tutorial to be incomplete, and I recently spent a couple days trying to get remote authentication to work. So I will provide my solution in case it helps someone in the future. I am using Ruby 2.2.2 and Rails 4.2.5.1.
First of all, this gist was an EXTREMELY helpful reference for me.
I also used the gem fakeweb to mock API calls.
Here are what my files look like:
app/models/user.rb
class User < include ActiveModel::Model
# required because some before_validations are defined in devise
include ActiveModel::Validations
# required to define callbacks
extend ActiveModel::Callbacks
# taken from http://stackoverflow.com/questions/8936906/whats-the-correct-way-to-make-before-validation-etc-work-in-an-activemodel
include ActiveModel::Validations::Callbacks
extend Devise::Models
# create getter and setter methods internally for the fields below
attr_accessor :email, :auth_token
#required by Devise
define_model_callbacks :validation
devise :remote_authenticatable, :timeoutable
# Latest devise tries to initialize this class with values
# ignore it for now
def initialize(options={})
end
end
lib/models/remote_authenticatable.rb
require 'fakeweb' #used for mocking API calls
module Devise
module Models
module RemoteAuthenticatable
extend ActiveSupport::Concern
#
# Here you do the request to the external webservice
#
# If the authentication is successful you should return
# a resource instance
#
# If the authentication fails you should return false
#
def remote_authentication(authentication_hash)
FakeWeb.register_uri(:get, "http://localhost:3000/webservice/login.json",
:body => "{ \"success\": \"true\", \"auth_token\": \"secure_token_123\", \"email\": \"bob#1123.com\"}")
# Your logic to authenticate with the external webservice
response = Net::HTTP.get(URI.parse("http://localhost:3000/webservice/login.json"))
self.email = JSON.parse(response)["email"]
self.auth_token = JSON.parse(response)["auth_token"]
return self
end
module ClassMethods
####################################
# Overriden methods from Devise::Models::Authenticatable
####################################
#
# This method is called from:
# Warden::SessionSerializer in devise
#
# It takes as many params as elements had the array
# returned in serialize_into_session
#
# Recreates a resource from session data
#
def serialize_from_session(data, salt)
resource = self.new
resource.email = data['email']
resource.auth_token = data['auth_token']
resource
end
#
# Here you have to return and array with the data of your resource
# that you want to serialize into the session
#
# You might want to include some authentication data
#
def serialize_into_session(record)
[
{
:email => record.email,
:auth_token => record.auth_token
},
nil
]
end
end
end
end
end
config/initializers/remote_authenticatable.rb
module Devise
module Strategies
class RemoteAuthenticatable < Authenticatable
#
# For an example check : https://github.com/plataformatec/devise/blob/master/lib/devise/strategies/database_authenticatable.rb
#
# Method called by warden to authenticate a resource.
#
def authenticate!
#
# authentication_hash doesn't include the password
#
auth_params = authentication_hash
auth_params[:password] = password
#
# mapping.to is a wrapper over the resource model
#
resource = mapping.to.new
return fail! unless resource
# remote_authentication method is defined in Devise::Models::RemoteAuthenticatable
#
# validate is a method defined in Devise::Strategies::Authenticatable. It takes
#a block which must return a boolean value.
#
# If the block returns true the resource will be logged in
# If the block returns false the authentication will fail!
#
# resource = resource.remote_authentication(auth_params)
if validate(resource){ resource = resource.remote_authentication(auth_params) }
success!(resource)
end
end
end
end
end
config/initializers/devise.rb
Devise.setup do |config|
# ...
# ...
# OTHER CONFIGURATION CODE HERE
# ...
# ...
# ==> 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
# BEGIN code that was added to this file
config.warden do |manager|
manager.strategies.add(:remote_authenticatable, Devise::Strategies::RemoteAuthenticatable)
manager.default_strategies(:scope => :user).unshift :remote_authenticatable
end
Devise.add_module :remote_authenticatable, :controller => :sessions, :route => { :session => :routes }
# END code that was added to this file
# ...
# ...
# OTHER CONFIGURATION CODE HERE
# ...
# ...
end
config/application.rb
# ...
# ...
# OTHER CODE HERE
# ...
# ...
module RemoteAuth
class Application < Rails::Application
# ...
# OTHER CODE HERE
# ...
# BEGIN code that was added to this file
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.autoload_paths += Dir["#{config.root}/app/models/**/"]
# END code that was added to this file
end
end
This Gist shows you exactly how to do this. There are a few extra steps, like activating the :token_authenticatable module in your user model, and in config/devise.rb
I'm using devise gem and want to translate confirmation mail. I already got my own template and overridden mailer method:
class LocalizedDeviseMailer < Devise::Mailer
def confirmation_instructions(record, locale)
#locale = locale
super
end
end
So, in my template I can do something like that:
I18n.locale = #locale
And then:
t("it.really.works")
But I don't know how to pass my variable with locale to mailer method. What is the best way to do that? Any help would be appreciated.
Devise is offering the localisation of mail template "natively".
have a look at the devise source code
https://github.com/plataformatec/devise/blob/master/lib/devise/mailers/helpers.rb
In this file is explained how to localise the subject (to be added to your locales files)
# Setup a subject doing an I18n lookup. At first, it attemps to set a subject
# based on the current mapping:
#
# en:
# devise:
# mailer:
# confirmation_instructions:
# user_subject: '...'
#
This is then the body template that you need to localise as any other html.erb
https://github.com/plataformatec/devise/blob/master/app/views/devise/mailer/confirmation_instructions.html.erb
Depending if your new user will sign_up using http://yoursite/it/users/sign_up or http://yoursite/en/users/sign_up (as you would normally do in your routes for your localised application) the good localised subject and mail (in the former case in Italian, in the latter in English) will be sent.
I recommend to add a locale column to your User model and use your own mailer.
This way you have also more flexibility, if you plan to set your own stylesheets and from fields or add additional mails.
in config/initializer/devise.rb:
Devise.setup do |config|
...
config.mailer = "UserMailer"
...
end
in app/mailers/user_mailer.rb
class UserMailer < Devise::Mailer
default from: "noreply#yourdomain.com"
def confirmation_instructions(user)
#user = user
set_locale(#user)
mail to: #user.email
end
def reset_password_instructions(user)
#user = user
set_locale(#user)
mail to: #user.email
end
def unlock_instructions(user)
#user = user
set_locale(#user)
mail to: #user.email
end
private
def set_locale(user)
I18n.locale = user.locale || I18n.default_locale
end
end
One more way is to add the initializer:
require 'devise/mailer'
module Devise
class Mailer
module Localized
%w(
confirmation_instructions
reset_password_instructions
unlock_instructions
).each do |method|
define_method(method) do |resource, *args|
I18n.with_locale(resource.try(:locale)) do
super(resource, *args)
end
end
end
end
prepend Localized
end
end
For ruby <2.1 you can use concern with alias_method_chain.
I think the simplest way to do that is add it on record. So you can add a locale column in your User or juste add an attr_accessor :locale in your User model
So you just need define this locale in your record and use it with I18n.locale = record.locale