[edit] I can delay all mail using delayed_job plugin on a shared server with a daemon… except the mail using authlogic.
I don't know where I have to search, and why it is working in other way.
daemons (off) delayed_job & delayed_mail_mailer (on) authlogic (reset_password) : send
daemons (on) delayed_job & delayed_mail_mailer (on) other_model (send_mail) : send
daemons (on) delayed_job & delayed_mail_mailer (on) authlogic (reset_password) : nothing !
Where to search the problem ??
app/initializer/delayed_mailer.rb :
class ActionMailer::Base
include Delayed::Mailer
end
config/initializers/delayed_job_config.rb :
Delayed::Job.destroy_failed_jobs = false
silence_warnings do
Delayed::Job.const_set("MAX_ATTEMPTS", 3)
Delayed::Job.const_set("MAX_RUN_TIME", 5.minutes)
end
script/dalayed_job :
#!/usr/bin/env ruby
require 'rubygems'
require 'daemons'
dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
daemon_options = {
:multiple => false,
:dir_mode => :normal,
:dir => File.join(dir, 'tmp', 'pids'),
:backtrace => true,
:log_output => true
}
Daemons.run_proc('delayed_job', daemon_options) do
Dir.chdir dir
RAILS_ENV = ENV['RAILS_ENV'] || 'development'
require File.join('config', 'environment')
Delayed::Worker.new.start
end
model/controllers/passwoed_reset_controller.rb
def create
#user = User.find_by_email(params[:email])
if #user
Notifier::deliver_password_reset_instructions(#user)
flash[:notice] = t('ResetInstructionSend') + t('CheckMail')
redirect_to root_url
else
flash[:notice] = t('NoUserMail')
render :action => :new
end
end
controllers/other_controller.rb
def update
#patent = Patent.find(params[:id])
# update all
#patent.update_attributes(params[:patent])
#user = #patent.user
#skill = #patent.skill
#current_user = current_user
Notifier::deliver_specialist_confirmation(#user, #skill, #current_user)
end
models/notifier.rb
def password_reset_instructions(user)
recipients user.email
from "Skill Forest"
subject "Password Reset Instructions"
body :edit_password_reset_url => edit_password_reset_url(user.perishable_token),:user => user
end
def specialist_confirmation (user, skill, current_user)
recipients user.email
from "Skill Forest"
subject "Bravo"
body :user => user, :skill => skill, :current_user => current_user
end
More infos : Rails 2.3.5 & tobi delayed_job & daemons 1.0.10
I'm on dreamhost shared web hosting, but look the same on development mode on my computer.
[Edit 2] I will control to be sure, but seems it was only the max run time too short…
[Edit 3] an other way I'm trying : control if pid exist
Just to turn the page… passing in Rails 3 and updating gem… everything fine. And much simpler.
Mystery of binary coding !
Related
I want send an SMS each 5 minutes to my users. At the moment, my application sends an SMS during the creation of an account.
# users_controller.rb
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
#user.send_daily_sms
flash[:info] = "Veuillez contrôler votre boîte mail pour activer votre compte."
redirect_to root_url
else
render 'new'
end
end
# user.rb
def send_daily_sms
# put your own credentials here
account_sid = '**********************'
auth_token = '**********************'
# set up a client to talk to the Twilio REST API
#client = Twilio::REST::Client.new account_sid, auth_token
#client.account.messages.create({
:from => '**********',
:to => '***********',
:body => 'Salut',
})
end
I already have scheduled mails working in my project by doing this :
# schedule.rb
every :day, :at => '12pm' do
rake "email_sender_daily"
end
# My task
task :email_sender_daily => :environment do |_, args|
User.find_each do |user|
UserMailer.daily_mail(user).deliver_now if user.daily == true
end
end
# My UserMailer
def daily_mail(user)
#user = user
mail to: user.email, subject: "Mail journalier"
end
I'm showing you this because, with the UserMailer, I know how to access it from an other file. Here, I'd like to do the exactly the same for SMS, but how can I access the method that is in my Model ? If not, where can I put this method to be able to access it from my rake task ?
Twilio developer evangelist here.
It looks to me like you have all the parts you need. If send_daily_sms is a method in your User class then all you require is a rake task like so:
task :sms_sender_daily => :environment do |_, args|
User.find_each do |user|
user.send_daily_sms if user.daily == true
end
end
And then your schedule.rb would look like:
every :day, :at => '12pm' do
rake "email_sender_daily"
rake "sms_sender_daily"
end
I would warn that sending sms messages to all your users via one method that calls the API over and over again is somewhat fragile. If one message fails to send because of a timeout or some other error then the task will throw an error and not be able to complete sending all the messages.
I'd suggest sending both emails and sms messages by workers using a background queue, like Rails's ActiveJob. If you are on the latest Rails 4.2 then you can use a gem called Textris that works much like ActionMailer and then you could define a UserTexter class like this:
class UserTexter < Textris::Base
default :from => YOUR_NUMBER
def daily_sms(user)
#user = user
text :to => #user.phone_number
end
end
Then your tasks could look like this:
task :email_sender_daily => :environment do |_, args|
User.find_each do |user|
UserMailer.daily_mail(user).deliver_later if user.daily == true
end
end
task :sms_sender_daily => :environment do |_, args|
User.find_each do |user|
UserTexter.daily_sms(user).deliver_later if user.daily == true
end
end
Check out the Textris documentation for more on how to use the gem.
Let me know if this helps at all!
I am trying to rewrite an older app that was created with PHP/MySQL.
The authentication system used has a users table in the database that stores username, email etc... but NOT passwords.
Whenever the user logs in it first checks the database to see if the user exists if not then returns a login error. If the user exists in the local database then it tries to bind to the active directory using the username/password combination entered by the user and creates a session if successful.
What is the best way to accomplish this using Rails?
Ruby's Net::LDAP library is pretty good.
Here's a simplified version of what I've been using for years:
# sessions_controller.rb
def create
user = User.find_by_login(params[:login])
if user && Ldap.authenticate(params[:login], params[:password])
self.current_user = user
Rails.logger.info "Logged in #{user.name}"
flash[:notice] = "Successfully Logged In!"
redirect_back_or_default root_url
else
flash[:alert] = "Invalid User credentials"
render :new
end
end
# lib/ldap.rb
# Ldap.authenticate('user','password')
# Returns true if validated
# Returns false if invalidated
# Returns nil if LDAP unavailable
require 'net/ldap'
class Ldap
def self.config
# this is actually loaded from a yaml config file
{
:domain => 'YOURDOMAIN',
:host => '10.10.10.100'
}
end
def self.authenticate(login, password)
conn = Net::LDAP.new(
:host => config[:host],
:port => 636,
:base => "dc=#{config[:domain]}, dc=local",
:encryption => :simple_tls,
:auth => {
:username => "#{login}##{config[:domain]}.local",
:password => password,
:method => :simple
}
)
Timeout::timeout(15) do
return conn.bind ? true : false
end
rescue Net::LDAP::LdapError => e
notify_ldap_admin(config[:host],'Error',e)
nil
rescue Timeout::Error => e
notify_ldap_admin(config[:host],'Timeout',e)
nil
end
def self.notify_ldap_admin(host,error_type,error)
msg = "LDAP #{error_type} on #{host}"
RAILS_DEFAULT_LOGGER.debug(msg)
DeveloperMailer.deliver_ldap_failure_msg(msg,error)
end
end
Check out the devise and devise_ldap_authenticatable libraries.
In User model I have following methods:
def generate_and_set_new_password!
password = random_pronouncable_token
self.password_hash = encrypt(password)
self.password_reset_sent_at = Time.now
if self.save!
password
else
nil
end
end
private
def encrypt(string)
# Some code that encrypts string
# ...
end
def random_pronouncable_token(size = 4)
# Some code that generates new password
# ...
end
rSpec test:
describe "New password generation" do
it "should generate new password" do
user = #user
old_pwd_hash = user.password_hash
pwd = user.generate_and_set_new_password!
pwd.should_not be_nil
user.password_hash.should_not == old_pwd_hash
end
end
This test gives me such output:
1) User password encryption New password generation should generate new password
Failure/Error: user.password_hash.should_not == old_pwd_hash
expected not: == "966abfff742d73c71fc87a1928b90aec"
got: "966abfff742d73c71fc87a1928b90aec"
' generate_and_set_new_password! ' method correctly works in app, here is example from console:
ruby-1.8.7-p249 :014 > user.password_hash
=> "ef10fb44dfceeaca05ca78030c1d8cb5"
ruby-1.8.7-p249 :015 > user.generate_and_set_new_password!
=> "quispopeho"
ruby-1.8.7-p249 :016 > user.password_hash
=> "a0b7605d5d6ca2152f7e019b4896fef4"
ruby-1.8.7-p249 :017 >
As we can see, it gives new password_hash for user, so it works correctly.
I decided that this is some bug in rSpec. I've made such experiment:
Here is some changes in test's code:
describe "New password generation" do
it "should generate new password" do
user = #user
old_pwd_hash = user.password_hash
p "old_password = ", user.password
p "old_pwd_hash = ", old_pwd_hash
pwd = user.generate_and_set_new_password!
p "received new password = #{pwd}"
p "SHOULD BE a new_pwd_hash, but it isn't! ", user.password_hash
p "SHOULD BE a new_password, but it isn't! ", user.password
pwd.should_not be_nil
# There I am tring to set this new password explicitly :)
user.password = pwd
user.save
p "NOW it is other password hash = ", user.password_hash
user.password_hash.should_not == old_pwd_hash
end
end
Now test is passed!
And the debugging output is the following:
"old_password = foobar"
"old_pwd_hash = c251e8a9bcc4fba1199181b1f2c1430c"
"received new password = kongibyty"
"SHOULD BE a new_pwd_hash, but it isn't! c251e8a9bcc4fba1199181b1f2c1430c"
"SHOULD BE a new_password, but it isn't! foobar"
"NOW it is other password hash = e2b53133b796bcb212a0c1fa1d2a1cfa"
As I can see from this debugging output, user object get it's new password_hash ONLY after it was EXPLICITLY set up in test's code. Seems like 'generate_and_set_new_password!' method can't change object that was created in test environment.
Why I am getting such weird rSpec's behaviour?
Any suggestions will be useful.
Some additional info about my development envoroment:
I am using:
ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]
Rails 3.0.9
rvm
Gemfile snippet:
group :development, :test do
gem "rspec-rails", "~> 2.6"
gem 'webrat', '0.7.1'
gem 'spork', '0.9.0.rc9'
gem 'autotest-standalone'
gem 'autotest', '4.4.6'
gem 'autotest-rails-pure', '4.1.2'
gem 'factory_girl_rails', '1.0'
end
Udpates:
Code that creates #user
require 'spec_helper'
describe User do
# attributes for user object
before(:each) do
#attr = {
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar",
:first_name => "Example",
:last_name => "Examploid",
:dob => Date.new(1980,07,03),
:relationships_status => "Single",
:sex => "male",
:sexual_orientaion => "Woman",
:about_me => "All about me",
:school_id => 1,
:education_id => 1,
:occupation_id => 1,
:web_site => "example.com"
}
end
# Some other tests...
# ...
describe "password encryption" do
# create user
before(:each) do
#user = User.create!(#attr)
end
# Some other tests...
# ...
# Here is shown the same test code as above.
describe "New password generation" do
it "should generate new password" do
# I even tried to use factory_girl's method
# #user = Factory(:device_user)
user = #user
old_pwd_hash = user.password_hash
pwd = user.generate_and_set_new_password!
pwd.should_not be_nil
user.password_hash.should_not == old_pwd_hash
end
end
end
end
Some clarification:
I am not using stubbing of #user's methods in any of my tests.
I tried to do the same with factory_girl, and got the same test's fail.
Here user's Factory definition:
Factory.define :device_user, :class => User do |user|
user.password "password"
user.password_confirmation "password"
user.email "device_user#example.com"
user.first_name "Device"
user.last_name "User"
user.dob Date.new(2011,10,03)
user.relationships_status "Single"
user.sex "male"
user.sexual_orientaion "Woman"
user.about_me "All about Device User"
user.school_id 1
user.education_id 1
user.occupation_id 1
user.web_site "device.example.com"
user.facebook_id "face_test_id"
end
This should be an easy one to track down...but it isn't proving that way for me:
I have the following cucumber scenario:
Scenario: Send mail
Given I am a guest
When I go to the new_contact page
And I fill in "contact_name" with "Test User"
And get mail count
And I fill in "contact_email" with "test#example.com"
And I fill in "contact_message" with "Test Message"
And I fill in "contact_phone_num" with "123456789"
And I press "Send Message"
And get mail count
All default steps except for "get mail count", which simply returns:
puts ActionMailer::Base.deliveries.count
The first step of "get mail count" returns zero, the second returns 2. Running ActionMailer::Base.deliveries confirms the email is identical (including object identifier). I cannot, for the life of me, figure out where that second send is coming from. When actually using the app, the mail only comes through once. Relevant code below:
Controller:
class ContactsController < ApplicationController
def new
#contact = Contact.new
#pagetitle = "Contact Us"
if (current_user) then
#contact.name = "#{current_user.first_name} #{current_user.last_name}"
#contact.email = current_user.email
end
end
def create
#contact = Contact.new(params[:contact])
if #contact.save
contactmailer = ContactMailer
puts 'here now'
contactmailer.contact_message(#contact).deliver
redirect_to contact_thanks_url, notice: 'Contact was successfully created.'
else
render action: "new"
end
end
def thanks
end
end
Mailer:
class ContactMailer < ActionMailer::Base
def contact_message(contact)
#contact = contact
mail(:to => ENV['MANAGER_EMAIL'], :from => #contact.email, :subject => t(:business_name), :content_type => 'text/plain')
end
end
Cucumber Config File:
BC::Application.configure do
require 'ruby-debug'
config.cache_classes = true
config.use_transactional_fixtures = true
config.serve_static_assets = true
config.static_cache_control = "public, max-age=3600"
config.whiny_nils = true
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.action_dispatch.show_exceptions = false
config.action_controller.allow_forgery_protection = false
config.action_mailer.delivery_method = :test
config.active_support.deprecation = :stderr
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
config.action_mailer.raise_delivery_errors = true
config.action_mailer.perform_deliveries = true
ENV['MANAGER_EMAIL'] = 'test#example.com'
end
Answer in case anyone has the same issue:
email_spec gem. The 'require' statement in support/features/env.rb was double-calling the mailer. Why I'm not sure, but I uninstalled the gem & everything worked fine.
Is there a exception_notification-like gem for delayed_job?
Preferably that works with REE-1.8.7 and Rails 2.3.10.
I've done something like this in the past for delayed job rake tasks:
require 'action_mailer'
class ExceptionMailer < ActionMailer::Base
def setup_mail
#from = ExceptionNotifier.sender_address
#sent_on = Time.now
#content_type = "text/plain"
end
def exception_message(subject, message)
setup_mail
#subject = subject
#recipients = ExceptionNotifier.exception_recipients
#body = message
end
end
namespace :jobs do
desc "sync the local database with the remote CMS"
task(:sync_cms => :environment) do
Resort.sync_all!
result = Delayed::Job.work_off
unless result[1].zero?
ExceptionMailer.deliver_exception_message("[SYNC CMS] Error syncing CMS id: #{Delayed::Job.last.id}", Delayed::Job.last.last_error)
end
end
end
Include this module in classes which are to be delayed:
require 'exception_notifier'
module Delayed
module ExceptionNotifier
# Send error via exception notifier
def error(job, e)
env = {}
env['exception_notifier.options'] = {
:sections => %w(backtrace delayed_job),
:email_prefix => '[Delayed Job ERROR] ',
:exception_recipients => %w(some#email.com),
:sender_address => %(other#email.com)
}
env['exception_notifier.exception_data'] = {:job => job}
::ExceptionNotifier::Notifier.exception_notification(env, e).deliver
end
end
end
and create a template for the notification in app/views/exception_notifier/_delayed_job.text.erb:
Job name: <%= #job.name %>
Job: <%= raw #job.inspect %>
* Process: <%= raw $$ %>
* Server : <%= raw `hostname -s`.chomp %>