I can't seem to make the whenever gem work with my actionmailer. I am trying to run the application in development wherein an email would be sent to a specific individual at a specific time.
In my schedule.rb file I have the following:
every :day, :at => '12:48pm' do
runner "FoodPlan.food_email"
end
In my controller called food_plans_controller.rb I have:
def self.food_email
#food_plans = FoodPlan.where(food_plan_date: Date.today).order('meal ASC')
UserMailer.food_email(#food_plans).deliver_now
end
In user_mailer.rb I have (take note I removed the email for privacy reasons) :
def food_email (food)
#food_plans = food
mail(to: '---------#yahoo.com', subject: 'Food Orders for #{Date.today}')
end
I have a folder in the views called user_mailer, inside it is a file called food_email.html.erb with the ff:
<!DOCTYPE html>
<html>
</head>
<body>
<h1>title</h1>
<p>
Good morning, -----!
<br><br>
Meal Plans for <%=Date.today%>:
<br><br>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>#</th>
<th>Meal Plan</th>
<th>Ordered by</th>
<th>Room Number</th>
<th>Received by</th>
<th>Signature</th>
</tr>
</thead>
<tbody>
<%countint=1%>
<% #food_plans.each do |food_plan| %>
<tr>
<td><%=countint%></td>
<td><%= food_plan.meal %></td>
<td><%=food_plan.applicant.first_name%>
<%=food_plan.applicant.last_name%></td>
<td><%=food_plan.applicant.room_number%></td>
<%countint+=1%>
<td></td>
<td></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<br>
---.
<br>
<br>
If you have any concerns, don't hesitate to call us at ------.
<br>
<br>
Thanks, <br>----
</p>
</body>
</html>
In my development config I have (I removed the email and password):
config.action_mailer.smtp_settings = {
address: 'smtp.gmail.com',
port: 587,
domain: 'example.com',
user_name: '-------',
password: '-------',
authentication: 'plain',
enable_starttls_auto: true }
I have tried reading this guide but I cannot still get the gem to work with actionmailer. I have also read up on the documentation of the whenever gem but I still can't figure out what I'm doing wrong. :(
I'm not getting any errors, it's just that the email is not sending.
I guess your whenever rules did not ever get to the local crontab and thus are never actually run. The whenever gem rules do not make the system run the commands by theselves, they are only a ruby notation of the cron rules that reside in the /etc/crontab (or similar) on unix-like systems.
The whenever gem automatically updates the crontab during deployment (using the capistrano plugin), so your rules should work on the production server.
On the development host however, you need to update your crontab manually (credits):
whenever --update-crontab --set environment='development'
To view what's currently inside your crontab, use cat /etc/crontab. Your rules, defined in whenever, should be present in the file after you've updated it.
Related
I am having trouble to attach files to inputs which use direct upload inside my system tests (Capybara). All tests worked before I switched to direct upload. I have also tried to manually submit appropriate forms via Browser and everything works there. Unfortunately, no luck with Capybara :/.
Inside view, I have following input:
<%= f.input :desktop_files, as: :file, input_html: { direct_upload: true, multiple: true } %>
and file is attached to input in system test by:
attach_file 'uploads_create_assets_former[desktop_files][]', "#{fixture_path}/files/image.jpg"
When I try to run test which uses similar piece of code, I get:
Selenium::WebDriver::Error::UnexpectedAlertOpenError: unexpected alert open: {Alert text : Error reading image.jpg}
(Session info: headless chrome=94.0.4606.81)
and when I check console inside browser opened by Capabyra, I can see following error:
FileReader error
My suspicion is that Capabyra/Selenium has problem to access attached file, but I don't know about any other way how to assign file to input. Maybe there is some Capybara magic which comes to play here :) -- hopefully, I am not only one who uses Rails direct upload and needs to test this piece of code with system tests...
I am using:
ruby (3.0.0)
rails (6.1.4.1)
selenium-webdriver (4.0.3)
capybara (3.35.3)
webdrivers (4.7.0)
and for capybara:
Capybara.register_driver :headless_chrome do |app|
options = Selenium::WebDriver::Chrome::Options.new(
args: %w[headless disable-gpu no-sandbox window-size=1440x768]
)
options.add_preference(:download, prompt_for_download: false,
default_directory: Rails.root.join('tmp/downloads').to_s)
options.add_preference(:browser, set_download_behavior: { behavior: 'allow' })
Capybara::Selenium::Driver.new(app, browser: :chrome, capabilities: options)
end
Edit:
Html code of form which should do upload looks like this:
<form class="formtastic uploads_create_assets_former" id="new_uploads_create_assets_former" enctype="multipart/form-data" action="/admin/upload/create" accept-charset="UTF-8" method="post">
<fieldset class="inputs">
<ol>
<li class="file input optional" id="uploads_create_assets_former_desktop_files_input"><label for="uploads_create_assets_former_desktop_files" class="label">Dateien (Computer)</label>
<input id="uploads_create_assets_former_desktop_files" multiple="multiple" data-direct-upload-url="http://127.0.0.1:49538/rails/active_storage/direct_uploads" type="file" name="uploads_create_assets_former[desktop_files][]" />
</li>
</ol>
</fieldset>
<fieldset class="actions">
<ol>
<li class="action input_action " id="uploads_create_assets_former_submit_action">
<input type="submit" name="commit" value="Nächster Schritt" data-disable-with="Nächster Schritt" />
</li>
</ol>
</fieldset>
</form>
I have not deviated in any way from Active Storage direct upload documented at https://edgeguides.rubyonrails.org/active_storage_overview.html#direct-uploads. Upload of files starts on former submission.
Another edit:
I have prepared minimalistic Rails app where you can try to play with my issue: https://github.com/martintomas/capybara-direct-upload. I have double checked that path is correct (otherwise Capybara::FileNotFound is raised), tried relative and absolute paths. I have also checked that anybody can read file:
-rw-r--r-- 1 martintomas staff 26436 Oct 22 12:51 image.jpg
Same problem happens when tests are run on my local machine or inside CI environment. To be honest, I have run out of ideas so I have decided to go for hacky solution now.
Hacky solution:
If you absolute trust active storage direct upload implementation and you don't have extra js code related to direct upload, you can turn it off inside system tests.
def attach_file(locator = nil, paths, make_visible: nil, **options)
turn_off_direct_upload # Capybara does not work with direct upload
super
end
def turn_off_direct_upload
page.execute_script 'document.querySelectorAll("input[data-direct-upload-url]:not([data-direct-upload-url=\"\"])").forEach((input) => { delete input.dataset.directUploadUrl } )'
end
I'm trying to mimic what a previous developer did to parse an XML file in my Rails app and am stuck. From what I can tell, my job completes, but nothing is being posted as it should be, so I'm guessing my parsing file is incorrect (however, it works fine when testing with the raw file on my localhost). So, where am I going wrong here?
This is Sidekiq log output, just to confirm job is happening and not showing any errors in processing:
2016-05-25T13:51:04.499Z 8977 TID-oxs3s9lng ParseTestData JID-2a01971539c887cac3bf3374:1 INFO: start
2016-05-25T13:51:04.781Z 8977 TID-oxs3s9l3g GenerateNotifications JID-2a01971539c887cac3bf3374:2 INFO: start
2016-05-25T13:51:04.797Z 8977 TID-oxs3s9lng ParseTestData JID-2a01971539c887cac3bf3374:1 INFO: done: 0.297 sec
2016-05-25T13:51:04.824Z 8977 TID-oxs3s9l3g GenerateNotifications JID-2a01971539c887cac3bf3374:2 INFO: done: 0.043 sec
This is my Sidekiq job file, which iterates through the compressed files that get submitted through my API. The file in question that I'm working on is nmap_poodle_scan.xml:
class ParseTestData
include Sidekiq::Worker
# Order matters. Parse network hosts first to ensure we uniquely identify network hosts by their mac address.
PARSERS = {
"network_hosts.xml" => Parsers::NetworkHostParser,
"nmap_tcp_service_scan.xml" => Parsers::TcpServiceScanParser,
"nmap_shellshock_scan.xml" => Parsers::ShellshockScanParser,
"hydra.out" => Parsers::HydraParser,
"events.log" => Parsers::EventParser,
"nmap_poodle_scan.xml" => Parsers::PoodleScanParser
}
def perform(test_id)
test = Test.find(test_id)
gzip = if Rails.env.development?
Zlib::GzipReader.open(test.data.path)
else
file = Net::HTTP.get(URI.parse(test.data.url))
Zlib::GzipReader.new(StringIO.new(file))
end
# Collect entries from tarball
entries = {}
tar_extract = Gem::Package::TarReader.new(gzip)
tar_extract.rewind
tar_extract.each do |entry|
entries[File.basename(entry.full_name)] = entry.read
end
# Preserve parse order by using the parser hash to initiate parser executions.
PARSERS.each_pair do |filename, parser|
next unless entry = entries[filename]
parser.run!(test, entry)
end
end
end
Which grabs nmap_poodle_scan.xml:
<host starttime="1464180941" endtime="1464180941"><status state="up" reason="arp-response" reason_ttl="0"/>
<address addr="10.10.10.1" addrtype="ipv4"/>
<address addr="4C:E6:76:3F:2F:77" addrtype="mac" vendor="Buffalo.inc"/>
<hostnames>
<hostname name="DD-WRT" type="PTR"/>
</hostnames>
Nmap scan report for DD-WRT (10.10.10.1)
<ports><extraports state="closed" count="996">
<extrareasons reason="resets" count="996"/>
</extraports>
<table key="CVE-2014-3566">
<elem key="title">SSL POODLE information leak</elem>
<elem key="state">VULNERABLE</elem>
<table key="ids">
<elem>OSVDB:113251</elem>
<elem>CVE:CVE-2014-3566</elem>
</table>
<table key="description">
<elem> The SSL protocol 3.0, as used in OpenSSL through 1.0.1i and
other products, uses nondeterministic CBC padding, which makes it easier
for man-in-the-middle attackers to obtain cleartext data via a
padding-oracle attack, aka the "POODLE" issue.</elem>
</table>
<table key="dates">
<table key="disclosure">
<elem key="year">2014</elem>
<elem key="month">10</elem>
<elem key="day">14</elem>
</table>
</table>
<elem key="disclosure">2014-10-14</elem>
<table key="check_results">
<elem>TLS_RSA_WITH_3DES_EDE_CBC_SHA</elem>
</table>
<table key="refs">
<elem>https://www.imperialviolet.org/2014/10/14/poodle.html</elem>
<elem>http://osvdb.org/113251</elem>
<elem>https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3566</elem>
<elem>https://www.openssl.org/~bodo/ssl-poodle.pdf</elem>
</table>
</table>
</script></port>
</ports>
<times srtt="4665" rttvar="556" to="100000"/>
</host>
Which should submit to PoodleScanParser:
module Parsers
class PoodleScanParser < NmapScanParser
def self.run!(test, content)
super(test, content, "//host//ports[.//elem[#key='state'][contains(text(), 'VULNERABLE')]]") do |host, network_host_test|
logger.info "Something cool"
IssueFinder.match(cve_id: "CVE-2014-3566").each do |issue|
Result.generate!(network_host_test.id, issue.id)
end
end
end
end
end
Which inherits from NmapScanParser. This file is parser is confirmed to work fine, so I know it's not the issue:
module Parsers
class NmapScanParser
def self.run!(test, content, xpath)
document = Nokogiri::XML(content)
document.remove_namespaces!
document.xpath(xpath).each do |host|
ip_address = host.at_xpath("address[#addrtype='ipv4']").at_xpath("#addr").value
vendor = host.at_xpath("address[#addrtype='mac']").at_xpath("#vendor").value rescue "Unknown"
hostname = host.at_xpath("hostnames/hostname").at_xpath("#name").value rescue "Hostname Unknown"
os = host.at_xpath("os/osmatch").at_xpath("#name").value rescue "Unknown"
os_vendor = host.at_xpath("os/osmatch/osclass").at_xpath("#vendor").value rescue "Unknown"
network_host_test = NetworkHostTest.generate!(test, ip_address: ip_address, hostname: hostname, vendor: vendor, os: os, os_vendor: os_vendor)
# If we didn't find a network host, that's because our network_hosts file didn't have this entry.
next unless network_host_test
yield(host, network_host_test)
end
end
end
end
I've confirmed the parser works on my localhost with the same raw output as above using a plain ruby file, and running ruby poodle_parser.rb:
require 'nokogiri'
document = Nokogiri::XML(File.open("poodle_results.xml"))
document.remove_namespaces!
document.xpath("//host[.//elem/#key='state']").each do |host|
ip_address = host.at_xpath("address[#addrtype='ipv4']").at_xpath("#addr").value
result = host.at_xpath("//ports//elem[#key='state']").content
puts "#{ip_address} #{result}"
end
Which outputs what I would expect in terminal:
10.10.10.1 VULNERABLE
So, in the end, I expect a Result to be generated, but it's not. I'm not seeing any errors in the Rails log on my localhost, nor am I seeing anything indicating an error in the Sidekiq logs either!
I decided to add a logger.info line to my PoodleScanParser to see if the Parser is even running as it should be. Assuming I did this correctly, the Parser doesn't look like it's running.
Well, the answer has nothing to do with Sidekiq, instead it was the output, which Nokogiri was dying on. Turns out Nmap was adding a non-XML line at the beginning of the XML file "Starting Nmap 7.12". So, Nokogiri was simply dying there.
I guess moral of the story is to make sure your XML output is what you Nokogiri intends it to be!
I'm having a strange issue when deploying my app to heroku.
So, there are two ways to create orders. First, whenever a payment is made, an order is automatically created. This is done entirely through Rails. Second, the Orders index view consists of a table which displays all of the orders, with a form to create new orders, which uses Angular. Really, it's more an experiment to practice using Angular.
So, the whole thing works fine in development. All of the orders (created through both methods) show up just fine, along with the corresponding user id, product id, and the total (the amount of money the order would cost).
When I deploy the app, however, I'm having a strange error. Everything works fine, except the totals for orders created with payments aren't showing up. That means, for all orders, the user id as well as product id are showing up, and for orders created with Angular, the order total is showing up. Only for orders created without Angular, the order total isn't appearing.
Some help would be greatly appreciated!!
assets/javascript/angular_app.js
var app = angular.module('shop', ['ngResource']);
app.factory('models', ['$resource', function($resource){
var orders_model = $resource("/orders/:id.json", {id: "#id"});
var products_model = $resource("/products/:id.json", {id: "#id"});
var users_model = $resource("/users/:id.json", {id: "#id"});
var x = {
orders: orders_model,
products: products_model,
users: users_model
};
return x;
}]);
app.controller('OrdersCtrl', ['$scope', 'models', function($scope, models){
$scope.orders = models.orders.query();
$scope.products = models.products.query();
$scope.users = models.users.query();
$scope.addOrder = function(){
order = models.orders.save($scope.newOrder, function(){
recent_order = models.orders.get({id: order.id});
$scope.orders.push(recent_order);
$scope.newOrder = '';
});
}
$scope.deleteOrder = function(order){
models.orders.delete(order);
$scope.orders.splice($scope.orders.indexOf(order), 1);
}
}]);
orders_controller.rb
class OrdersController < ApplicationController
protect_from_forgery
skip_before_action :verify_authenticity_token, if: :json_request?
respond_to :json, :html
def index
#orders = Order.all.to_json(:include => [{:product => {:only => :name}},
{:user => {:only => :email}}])
respond_with #orders
end
def show
#order = Order.find(params[:id]).to_json(:include => [{:product => {:only => :name}},
{:user => {:only => :email}}])
respond_with #order
end
def new
end
def create
#order = Order.create(order_params)
#order.product = Product.find(params[:product_id])
#order.user = User.find(params[:user_id])
OrderMailer.order_confirmation(#order.product, #order.user.email, #order.user.first_name)
respond_with #order
end
def destroy
respond_with Order.destroy(params[:id])
end
protected
def json_request?
request.format.json?
end
private
def order_params
params.require(:order).permit(:product_id, :user_id, :total)
end
end
orders/index.html.erb
<div ng-controller="OrdersCtrl">
<table class="table table-hover">
<thead>
<td>Order ID</td>
<td>Total</td>
<td>Product</td>
<td></td>
</thead>
<tr>
<form ng-submit="addOrder()">
<td>
<span class="form-control" disabled>
<%= Order.last.id + 1 %>
</span>
</td>
<td>
<input type="number" step="0.01" class="form-control" ng-model="newOrder.total">
</td>
<td>
<select ng-model="newOrder.product_id" class="form-control">
<option value="" disabled selected>Select a product</option>
<option ng-repeat="product in products" value="{{product.id}}">{{product.name}}</option>
</select>
</td>
<td>
<select ng-model="newOrder.user_id" class="form-control">
<option value="" disabled selected>Select a user</option>
<option ng-repeat="user in users" value="{{user.id}}">{{user.id}}</option>
</select>
</td>
<td>
<input type="submit" value="+" class="btn btn-success">
</td>
</form>
</tr>
<tr ng-repeat="order in orders | orderBy: '-id':reverse">
<td>
{{order.id}}
</td>
<td>
<strong>{{order.total | currency}}</strong>
</td>
<td>
{{order.product.name}}
</td>
<td>
{{order.user.email}}
</td>
<td>
<button ng-click="deleteOrder(order)" class="btn btn-danger btn-sm"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button>
</td>
</tr>
</table>
</div>
config/environments/production.rb
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests.
config.cache_classes = true
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
# and those relying on copy on write to perform better.
# Rake tasks automatically ignore this option for performance.
config.eager_load = true
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Enable Rack::Cache to put a simple HTTP cache in front of your application
# Add `rack-cache` to your Gemfile before enabling this.
# For large-scale production use, consider using a caching reverse proxy like
# NGINX, varnish or squid.
#config.action_dispatch.rack_cache = true
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
# Compress JavaScripts and CSS.
config.assets.js_compressor = :uglifier
# config.assets.css_compressor = :sass
# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = true
# Asset digests allow you to set far-future HTTP expiration dates on all assets,
# yet still be able to expire them through the digest params.
config.assets.digest = true
# `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
# Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
# Use the lowest log level to ensure availability of diagnostic information
# when problems arise.
config.log_level = :debug
# Prepend all log lines with the following tags.
# config.log_tags = [ :subdomain, :uuid ]
# Use a different logger for distributed setups.
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
# Use a different cache store in production.
config.cache_store = :dalli_store,
(ENV["MEMCACHIER_SERVERS"] || "").split(","),
{:username => ENV["MEMCACHIER_USERNAME"],
:passowrd => ENV["MEMCACHIER_PASSWORD"],
:failover => true,
:socket_timeout => 1.5,
:socket_failure_delay => 0.2
}
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = 'http://assets.example.com'
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true
# Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify
# Use default logging formatter so that PID and timestamp are not suppressed.
config.log_formatter = ::Logger::Formatter.new
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
config.assets.js_compressor = Uglifier.new(mangle: false)
end
I am using Ruby On Rails 3.2.13 Ruby 1.9.3.
I have the following code that I am using to display the messages Rails provides when validation is being done within a Model:
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
<%=t :the_form_contains %> <%= pluralize(object.errors.count, "#{t :error_text}") %>.
<ul>
<% object.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
I also have some customized text in my yaml files. Here is an example of how the messages appear on one server:
Le Formulaire contient 20 erreurs.
* Mot de passe doit être rempli(e)
* Prénom doit être rempli(e)
* Prénom est trop court (au moins 3 caractères)
* Prénom n'est pas valide
Here is how the messages appear on the other server:
Le Formulaire contient 20 erreurs.
* Mot de passe can't be blank
* Prénom can't be blank
* Prénom is too short (minimum is 3 characters)
* Prénom is invalid
Only the portions of the messages that I entered in the yaml files display properly. The ones that Rails would translate only work on one server.
I read the Rails Guide for I18n here http://guides.rubyonrails.org/i18n.html but did not see anything in config or anywhere else that I needed to do to get these working. As I stated it works fine on one server but not the other one.
If I did something on my development server that I did not do on my production server I do not remember what I did. I have checked config/application.rb and config/environment.rb on both servers and they are the same. Maybe there is something I need to initialize somewhere on my production server. If an answer is found it may also solve the problem that I asked about in Ruby on Rails I18n - Localization of Dates Works In localhost but Not In Production.
Any help would be appreciated. I will continue my research to see if I can find anything about this. So far I have found nothing.
UPDATE: 7/29/2013 12:47 pm CDT - The only other difference that I can see between the two servers is that the development server is running ruby 1.9.3p327 and the production server is running ruby 1.9.3p362. However I cannot believe that could be causing my problem but it is a difference that I feel I should note.
I could not find any other questions or comments relating to this. I decided to copy the fr.yml hashes for the rails-i18n gem. I did not have to do this on my development server to get the error messages to translate into French. As I stated it was working fine without them. When I deployed the new yaml file in production all my clauses are in French. I guess there is a needle in a haystack type of bug somewhere in the i18n process. At least the Rails translations for error messages are working now.
On one of application's page there is:
<% content_for :head do %>
<%= tag :meta, property: "fb:app_id", content: ENV["FACEBOOK_APP_ID"] %>
<% content_for :title, #check.title %>
<% end %>
And it worked in development, while I was running server at my localhost.
After that I deployed application on heroku. And that simply doesn't work.
What means doesn't work. I load page in development at localhost:
<title>Tenta</title>
<meta content="*****************" property="fb:app_id">
When I load this page deployed at heroku host, these tags are simply absent.
Why?
Also, If you don't know this particular issue solution, I would appreciate if you adivce me, how can I look what is going on - heroku logs and watching last 150 logs in inconvinient windows console doesn't give me a lot of helpful information.