I am new to Rails and I am faced with a problem:
I have to build an app for a Coworking Area. Freelancers complete a form and they can have one of these two statuses: accept or confirm.
I handle this with Enum type.
I can get their mails: Freelancer.confirm.map{ |freelancer| freelancer.email }
In Rails console:
Freelancer Load (2.8ms) SELECT "freelancers".* FROM "freelancers"
WHERE "freelancers"."status" = ? [["status", 0]] =>
["freelancer1#mail.com", "freelancer2#mail.com",
"freelancer3#mail.fr", "freelancer4#mail.com", "freelancer5#mail.fr",
"freelancer6#mail.com", "freelancer7#mail.com",
"freelancer8#mail.com", "freelancer9#mail.com",
"freelancer10#mail.com", "freelancer11#mail.com"]
But I can't use it correctly in my app:
In my app/models/freelancer.rb
class Freelancer < ApplicationRecord
enum status: [:confirm, :accept]
def send_contract_email
UserMailer.contract_email(self).deliver_now
end
end
In app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
def contract_email(freelancers)
#emails = freelancers.accept.map{|freelancer| freelancer.email}
mail(to: #emails, subject: 'Welcome to my site')
end
end
In lib/tasks/email_tasks.rake
desc 'send contract email'
task send_contract_email: :environment do
UserMailer.contract_email(freelancers).deliver_now
end
In config/environments/schedule.rb (set to 5 minutes for more convenience)
every '5 * * * *' do
rake 'send_contract_email'
end
And in app/views/user_mailer/contract_email.html.erb
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<h1>Hi!</h1>
</body>
</html>
When I run
rake send_contract_email
for testing everything works and checking whether the email is sent or not, it displays an error:
rake aborted! NameError: undefined local variable or method
freelancers' for main:Object
/home/gaelle/Bureau/corworking/coworking/lib/tasks/email_tasks.rake:3:in
block in '
/home/gaelle/.rvm/gems/ruby-2.4.1/gems/rake-12.1.0/exe/rake:27:in
<top (required)>'
/home/gaelle/.rvm/gems/ruby-2.4.1/bin/ruby_executable_hooks:15:in
eval'
/home/gaelle/.rvm/gems/ruby-2.4.1/bin/ruby_executable_hooks:15:in
`' Tasks: TOP => send_contract_email (See full trace by running
task with --trace)
The problem seems to be in UserMailer but I can not understand why I am wrong because I use Freelancer (with self) in the model.
Thanks for your help!
It looks like in your task, send_contract_email, you're using a local variable freelancers that hasn't been initialized. You'll need to set freelancers to be a list of freelancers in the task before calling UserMailer.contract_email, or else pass the list into the task as a parameter. Here's a nice post with information about how to pass parameters to rake tasks.
Related
Rails Viewcomponents allow you to test if a component has rendered in minitest using
refute_component_rendered but how do you do the same in RSpec?
class SometimesNotRenderedComponent < ViewComponent::Base
def initialize(my_param)
#my_param = my_param
end
def render?
# test this
end
end
it "renders nothing when..." do
render_inline(described_class.new(my_param))
# expect(page).to ... have no content
end
Let's dig a little, otherwise, the answer would be quite short.
The code for refute_component_rendered is pretty simple:
https://github.com/ViewComponent/view_component/blob/v2.78.0/lib/view_component/test_helpers.rb#L14
def refute_component_rendered
assert_no_selector("body")
end
assert_no_selector is a Capybara matcher. Negative matchers for rspec are defined dynamically with a prefix have_no_ and are not documented.
have_selector delegates to assert_selector.
https://www.rubydoc.info/gems/capybara/Capybara/RSpecMatchers#have_selector-instance_method
Which means have_no_selector delegates to assert_no_selector.
You can use either one, it's just a matter of preference:
it "does not render" do
render_inline(described_class.new(nil))
expect(page).to have_no_selector("body")
expect(page).not_to have_selector("body")
end
it "renders, but why" do
# why match on `body`? it is just how `render_inline` method works,
# https://github.com/ViewComponent/view_component/blob/v2.78.0/lib/view_component/test_helpers.rb#L56
# it assignes whatever the result of `render` to #rendered_content, like this:
#rendered_content = "i'm rendering"
# when you call `page`
# https://github.com/ViewComponent/view_component/blob/v2.78.0/lib/view_component/test_helpers.rb#L10
# it wraps #rendered_content in `Capybara::Node::Simple`
# https://www.rubydoc.info/gems/capybara/Capybara/Node/Simple
# if content is empty, there is no body
# Capybara::Node::Simple.new("").native.to_html
# # => "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n\n"
puts page.native.to_xhtml # to see what you're matching on
expect(page).to_not have_no_selector("body")
end
I've double checked:
$ bin/rspec spec/components/badge_component_spec.rb
BadgeComponent
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
does not render
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<body>
<p>i'm rendering</p>
</body>
</html>
renders, but why
Finished in 0.06006 seconds (files took 2.11 seconds to load)
2 examples, 0 failures
I'm using Capybara to test my project. But i have a problem.
I have some remote forms on my project. They add records via ajax. When i'm testing with capybara it works well on development environment. It visits the page, fills in the form and submits. Booom, record has been added and test didnt fail.
But when i run rspec with test environments i'm getting unknown format exception.
1) add new address user adds new address
Failure/Error: find("input[value='Adres Ekle']").click
ActionController::UnknownFormat:
Account::AddressesController#create is missing a template for this request format and variant.
request.formats: ["text/html"]
request.variant: []
# ./spec/features/user_add_new_address_spec.rb:28:in `block (2 levels) in <top (required)>'
I've also tried to respond via js from controller like;
def create
request.format = :js
end
Then it returns;
1) add new address user adds new address
Failure/Error: find("input[value='Adres Ekle']").click
ActionController::UnknownFormat:
Account::AddressesController#create is missing a template for this request format and variant.
request.formats: ["text/javascript"]
request.variant: []
# ./spec/features/user_add_new_address_spec.rb:28:in `block (2 levels) in <top (required)>'
And my scenario if u want more info;
scenario 'user adds new address' do
expect(page).to have_content 'Kayıtlı Adreslerim'
find("a[title='Adres Ekle']").click
expect(page).to have_content 'Yeni Adres Ekle'
expect(page).to have_content 'Adres Başlığı'
fill_in 'address[name]', with:'Izmir Ofisi'
select('Izmir', :from => 'address[city_id]')
fill_in 'address[address]', with: 'Lorem ipsum dolor sit amet.'
find("input[value='Adres Ekle']").click # It submits remote: true form.
expect(page).to have_content 'Success!'
end
PS: my create action doesnt render something like that.
its like;
def create
#new_address = Address.new
#address = #current_account.addresses.new(address_params)
if #address.save
#check = true
else
#check = false
end
end
it renders: create.js.erb
<% if #check %>
if($('.addresses').length) {
$('.addresses').append('<%= j(render('account/addresses/address', address: #address)) %>');
}
if($('#did-addresses').length){
$('#did-addresses').append("<%= "<option selected='true' value='#{#address.id}'>#{#address.name}</option>".html_safe %>").selectpicker('refresh');
}
$('#new-address').html('<%= j(render('account/addresses/form', new_address: #new_address)) %>');
swal({
type: 'success',
title: "<%= t('response.success') %>",
text: "<%= t('flash.actions.create.notice', resource_name: Address.model_name.human) %>",
timer: 2000
});
quickview.close('#new-address');
<% else %>
<% #address.errors.each do |error| %>
<% end %>
<% end %>
$('.preloader').fadeOut();
I was facing the same case in rails 6 but I fixed it be adding js: true to the scenario and it automatically worked well.
scenario 'Should delete the feature', js: true do
# Your logic
# Your expectations
end
Since copying your development config over your test config fixed your issue, it sounds like you probably an error in one of your JS files. Normally in the test and production environment all of your JS assets get concatenated into one file which means an error in any one of them can prevent the code in the others from being executed. In the development environment each JS file is loaded separately which means an error in any file can only affect the rest of the code in that file. Check your the console in your browser for any JS errors when going to the page in question and fix them.
I've been trying to get PDF's generated via sidekiq and wicked_pdf in a rails 5.1 app but keep getting this error:
2017-11-01T02:20:33.339Z 1780 TID-ovys2t32c GeneratePdfWorker JID-b3e9487113db23d65b179b1c INFO: start
2017-11-01T02:20:33.369Z 1780 TID-ovys2t32c GeneratePdfWorker JID-b3e9487113db23d65b179b1c INFO: fail: 0.03 sec
2017-11-01T02:20:33.371Z 1780 TID-ovys2t32c WARN: {"class":"GeneratePdfWorker","args":[2,1],"retry":false,"queue":"default","jid":"b3e9487113db23d65b179b1c","created_at":1509502833.334234,"enqueued_at":1509502833.3345}
2017-11-01T02:20:33.380Z 1780 TID-ovys2t32c WARN: NameError: undefined local variable or method `quote' for #<GeneratePdfWorker:0x007fb6d5cea070>
Did you mean? #quote
2017-11-01T02:20:33.380Z 1780 TID-ovys2t32c WARN: /Users/stefanbullivant/quottes/app/workers/generate_pdf_worker.rb:18:in `perform'
I get this error even with no locals being passed in the av.render method. Any ideas on what's causing it are appreciated.
quotes_controller.rb calling the worker
def create_pdf
#quote = Quote.find(params[:id])
GeneratePdfWorker.perform_async(#quote.id, current_account.id)
redirect_to #quote
end
Generate_pdf_worker.rb
class GeneratePdfWorker
include Sidekiq::Worker
sidekiq_options retry: false
def perform(quote_id, account_id)
#quote = Quote.find(quote_id)
#account = Account.find(account_id)
# create an instance of ActionView, so we can use the render method outside of a controller
av = ActionView::Base.new()
av.view_paths = ActionController::Base.view_paths
# need these in case your view constructs any links or references any helper methods.
av.class_eval do
include Rails.application.routes.url_helpers
include ApplicationHelper
end
pdf = av.render pdf: "Quote ##{ #quote.id } for #{ #quote.customer_name }",
file: "#{ Rails.root }/tmp/pdfs/quote_#{#quote.id}_#{#quote.customer_name}.pdf",
template: 'quotes/create_pdf.html.erb',
layout: 'layouts/quotes_pdf.html.erb',
disposition: 'attachment',
disable_javascript: true,
enable_plugins: false,
locals: {
quote: #quote,
account: #account
}
# pdf_html = av.render :template => "quotes/create_pdf.html.erb",
# :layout => "layouts/quotes_pdf.html.erb",
# :locals => {
# quote: #quote,
# account: #account
# }
# use wicked_pdf gem to create PDF from the doc HTML
quote_pdf = WickedPdf.new.pdf_from_string(pdf, :page_size => 'A4')
# save PDF to disk
pdf_path = Rails.root.join('tmp', "quote.pdf")
File.open(pdf_path, 'wb') do |file|
file << quote_pdf
en
end
end
quotes_pdf.html.erb PDF Layout
<!DOCTYPE html>
<html>
<head>
<title>Quottes</title>
<meta charset="utf-8" />
<meta name="ROBOTS" content="NOODP" />
<style>
<%= wicked_pdf_stylesheet_link_tag 'quote_pdf' -%>
</style>
</head>
<body>
<div class="container">
<%= yield %>
</div>
</body>
</html>
create_pdf.html.erb PDF View (for the sake of getting things running first, just two lines using each local)
<%= account.brand_name %>
<%= quote.customer_name %>
Any advice on getting this running is much appreciated. I have played around with simply generating plain html text in the pdf view without passing any variables and still get this error so I'm confused as to what's causing it.
I sometimes forget about this as well - It seems like it's very insistent that line 18 (which I can only assume is render) is looking up the quote local and can't find it, and #quote is defined at that time. If that is all your code, then I would presume the changes are not being picked up.
My best suggestion (which I hope works) is you need to restart sidekiq!
I have an app that lists tickets. It uses AngularJS. Here's the controller action:
def index
#tickets = apply_scopes(#tickets)
response.headers['x-total-count'] = #tickets.total_count
response.headers['x-per-page'] = Ticket.default_per_page
end
The Angular controller (Coffeescript):
$scope.fetch = ->
Ticket.query($scope.search).then (response) ->
$scope.tickets = response.data
$scope.totalCount = response.headers('x-total-count')
$scope.perPage = response.headers('x-per-page')
$scope.fetch()
I'm using angular-rails-resource to fetch the records. Everything works smoothly if I test by hand.
Here is the spec:
let(:user) { create :user }
scenario 'User lists tickets', js: true do
login_as user, scope: :user
ticket = create :ticket, user: user
visit root_path
click_on 'Support Requests'
expect(page).to have_content(ticket.subject)
end
When I run this spec, I just get the regular Rspec failure message because the condition was not met, but it should have:
expected to find text "ticket 000" in...
I figured it had something to do with concurrency and Capybara not waiting for Angular to fetch and display the records. Then I went ahead and added a sleep 2 right above the expectation just to test that. When I do it, I get a different error:
Capybara::Poltergeist::JavascriptError:
One or more errors were raised in the Javascript code on the page. If you don't care about these errors, you can ignore them by setting js_errors: false in your Poltergeist configuration (see documentation for details).
Possibly unhandled rejection: {"data":"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">\n<HTML>\n <HEAD><TITLE>Internal Server Error</TITLE></HEAD>\n <BODY>\n <H1>Internal Server Error</H1>\n undefined method `split' for 1:Fixnum\n <HR>\n <ADDRESS>\n WEBrick/1.3.1 (Ruby/2.3.3/2016-11-21) at\n 127.0.0.1:54674\n </ADDRESS>\n </BODY>\n</HTML>\n","status":500,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","url":"/tickets","params":{},"headers":{"Accept":"application/json","Content-Type":"application/json"},"data":{}},"statusText":"Internal Server Error "}
Possibly unhandled rejection: {"data":"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">\n<HTML>\n <HEAD><TITLE>Internal Server Error</TITLE></HEAD>\n <BODY>\n <H1>Internal Server Error</H1>\n undefined method `split' for 1:Fixnum\n <HR>\n <ADDRESS>\n WEBrick/1.3.1 (Ruby/2.3.3/2016-11-21) at\n 127.0.0.1:54674\n </ADDRESS>\n </BODY>\n</HTML>\n","status":500,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","url":"/tickets","params":{},"headers":{"Accept":"application/json","Content-Type":"application/json"},"data":{}},"statusText":"Internal Server Error "}
at http://127.0.0.1:54674/assets/application-713835b1641be632b29f7502c00a879e171bca5d6d06a5f264afcd819c123e76.js:14363
Here is my stack:
rails (5.0.2)
capybara (2.12.1)
poltergeist (1.13.0)
rspec-core (3.5.4)
phantomjs 2.1.1
Additional info:
If I output something right before ending the controller action, it gets outputted. The execution is going through the entire action;
If I console.log something right before fetching tickets, it's outputted as well. However, the Promise is not being resolved.
I found out the issue was with my pagination headers (x-total-count and x-per-page). Converting them to String does the trick. The weird part is that it was working OK in development, but not in test environment. So, if anyone has this issue in the future, the solution in my case was:
def index
#tickets = apply_scopes(#tickets)
response.headers['x-total-count'] = #tickets.total_count.to_s
response.headers['x-per-page'] = Ticket.default_per_page.to_s
end
Notice .to_s being called when assigning the headers.
I've created a mailer in my Rails 4 application, which works perfectly well when there are no attachments to the emails. However, when I add a pdf attachment to the emails, I get the following error when I try to preview the email at .../rails/mailers/donor_mailer/thankyou_mail_preview
NoMethodError in Rails::Mailers#preview
Showing /usr/local/rvm/gems/ruby-2.1.5#rails4/gems/railties-4.1.6/lib/rails/templates/rails/mailers/email.html.erb where line #95 raised:
<iframe seamless name="messageBody" src="?part=<%= Rack::Utils.escape(#part.mime_type) %>"></iframe>
However, I don't get this error, or any issues at all actually, when sending the emails themselves, only with the preview. Has any one else experienced this, and know how to fix this / knows the direction I should be heading in. Could this cause any further issues down the line with my emails, or is it just a problem with the preview feature?
My Mailer is as follows:
class DonorMailer < ActionMailer::Base
default from: "user#gmail.com"
def thankyou_email(computer)
#computer = computer
#donor = computer.donor
attachments["some.pdf"] = File.read("path/to/pdf/some.pdf")
mail(to: #donor.donor_email, subject: "Thank you")
end
end
and template is:
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<p>Email content here</p>
</body>
</html>
I tried adding :pdf to my MIME types to no avail.
Thank you