Wrong number of arguments error with TestMailer - ruby-on-rails

I'm running a strange problem sending emails. I'm getting this exception:
ArgumentError (wrong number of arguments (1 for 0)):
/usr/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/base.rb:642:in `initialize'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/base.rb:642:in `new'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/base.rb:642:in `create'
/usr/lib/ruby/gems/1.8/gems/ar_mailer-1.3.1/lib/action_mailer/ar_mailer.rb:92:in `perform_delivery_activerecord'
/usr/lib/ruby/gems/1.8/gems/ar_mailer-1.3.1/lib/action_mailer/ar_mailer.rb:91:in `each'
/usr/lib/ruby/gems/1.8/gems/ar_mailer-1.3.1/lib/action_mailer/ar_mailer.rb:91:in `perform_delivery_activerecord'
/usr/lib/ruby/gems/1.8/gems/actionmailer-2.1.1/lib/action_mailer/base.rb:508:in `__send__'
/usr/lib/ruby/gems/1.8/gems/actionmailer-2.1.1/lib/action_mailer/base.rb:508:in `deliver!'
/usr/lib/ruby/gems/1.8/gems/actionmailer-2.1.1/lib/action_mailer/base.rb:383:in `method_missing'
/app/controllers/web_reservations_controller.rb:29:in `test_email'
In my web_reservations_controller I have a simply method calling
TestMailer.deliver_send_email
And my TesMailer is something like:
class TestMailer < ActionMailer::ARMailer
def send_email
#recipients = "xxx#example.com"
#from = "xxx#example.com"
#subject = "TEST MAIL SUBJECT"
#body = "<br>TEST MAIL MESSAGE"
#content_type = "text/html"
end
end
Do you have any idea?
Thanks!
Roberto

The problem is with the model that ar_mailer is using to store the message. You can see in the backtrace that the exception is coming from ActiveRecord::Base.create when it calls initialize. Normally an ActiveRecord constructor takes an argument, but in this case it looks like your model doesn't. ar_mailer should be using a model called Email. Do you have this class in your app/models directory? If so, is anything overridden with initialize? If you are overriding initialize, be sure to give it arguments and call super.
class Email < ActiveRecord::Base
def initialize(attributes)
super
# whatever you want to do
end
end

Check that email_class is set correctly: http://seattlerb.rubyforge.org/ar_mailer/classes/ActionMailer/ARMailer.html#M000002
Also don't use instance variables. Try:
class TestMailer < ActionMailer::ARMailer
def send_email
recipients "roberto.druetto#gmail.com"
from "roberto.druetto#gmail.com"
subject "TEST MAIL SUBJECT"
content_type "text/html"
end
end
From the docs: the body method has special behavior. It takes a hash which generates an instance variable named after each key in the hash containing the value that that key points to.
So something like this added to the method above:
body :user => User.find(1)
Will allow you to use #user in the template.

Related

Rails does not recognize model method

I have a method on my model and it actually was created as advised(with self.method_name). However, when I go to my console and try to test the method I get an undefined method error.
Does anyone know why?
my Model
class Country < ApplicationRecord
has_many :facts
has_many :dishes
has_many :touristic_places
include HTTParty
base_uri 'restcountries.eu/rest/v2/region/africa'
def self.save_data_from_api
response = HTTParty.get(base_uri)
country_data = JSON.parse(response)
countries = country_data.map do |line|
c = Country.new
c.name = line.name
c.save
c
end
countries.select(&:persisted?)
end
On my Controller
def save_data_from_api
countrie = Country.save_data_from_api
end
Test on Rails console error:
> Country.save_data_from_api
Traceback (most recent call last):
2: from (irb):5
1: from (irb):5:in `rescue in irb_binding'
NoMethodError (undefined method `save_data_from_api' for #<Class:0x00007fc9ec71edd8>)
Don't do HTTP calls in your model. Even without any code your model already has a ton of responsibilities that it gets from ActiveRecord::Base:
validations
assocations
persistence
callbacks
naming
i18n
Instead create a separate client object that fetches the data from the API. This gives you an object which just does one job which is both easy to test and stub out:
# app/clients/rest_countries_client.rb
# HTTP client for the restcountries.eu API
class RestCountriesClient
include HTTParty
base_uri 'restcountries.eu/rest/v2'
format :json # this will automatically parse the response
def self.region(region)
get("/region/#{region}")
end
end
This lets you just test the API call from the console by calling RestCountriesClient.region('africa') and you can see the returned hash without any side-effects.
To actually do the call and persist the objects you want to use a service object or ActiveJob:
# app/jobs/country_importer_job.rb
# Persists countries from the restcountries.eu API
class CountryImporterJob < ApplicationJob
def perform(region = 'africa')
response = RestCountriesClient.region(region)
return unless response.success?
response.map do |line|
Country.create(name: line["name"])
end.select(&:persisted?)
end
end
You would then call this job from the controller:
CountryImporterJob.perform_now('africa')
Quit the console and start it again. Auto-reload only works in the server, not the console.

Rails Console Argument Error Creating a New object

I am new to Rails and Ruby development but I am trying to create an object called Currency which takes in two params and does some calculations on them. I am using attr_accessor to set up the params and I put the file inside the lib directory.
Whenever I run rails console and try to do c = Currency.new(100, "CAD") I get the following error:
ArgumentError: wrong number of arguments (given 2, expected 0)
from (irb):5:in `initialize'
from (irb):5:in `new'
from (irb):5
I did make sure to include the file in application.rb. Here is a skeleton of my class:
class Currency
class << self
attr_accessor :input_value, :currency_iso
USD_ISO = "USD"
USD_TO_DM = 2.8054
def converted_value
convert_to_dm
end
private
def convert_to_dm
#input_value / USD_TO_DM
end
end
end
I have looked all over and I am stumped on what this issue may be. I have tried with and without an initialize method and I have tried creating a more basic version.
The problem here is that you are defining the method as a class method. And you are not defining the initialize method with those two params. Let's check the code below:
class Currency
attr_accessor :input_value, :currency_iso
USD_ISO = "USD"
USD_TO_DM = 2.8054
def initialize(input_value, currency_iso)
#input_value = input_value
#currency_iso = currency_iso
end
def converted_value
convert_to_dm
end
private
def convert_to_dm
input_value / USD_TO_DM
end
end
Also, due to you have already defined the attr_accessor you don't need to use the # when calling those attributes.
I found this post. It can help you to understand better the difference between class method and instance method.

Assert Redis publication

I am writing a spec for an after_create callback. The spec looks like this:
it 'broadcasts creation' do
message = Message.create(body: 'foo')
expect(Redis.any_instance).to have_received(:publish)
end
My Message model looks like this:
class Message < ActiveRecord::Base
after_create -> { publish(:create) }
private
def publish(name)
Redis.new.publish(
self.class.inferred_channel_name,
json(action)
)
Redis.new.publish(
inferred_channel_name_for_single_record,
json(action)
)
puts 'published!'
end
end
I know that the callback runs because I am printing 'published' at the end, and I have verified that Redis indeed publishes something twice.
Nonetheless, my spec fails with the following message:
1) Message Methods #entangle without options broadcasts creation
Failure/Error: expect(Redis.any_instance).to have_received(:publish)
unstubbed, expected exactly once, not yet invoked: #<AnyInstance:Redis>.publish(any_parameters)
# ./spec/models/message_spec.rb:20:in `block (5 levels) in <top (required)>'
I am using bourne with mocha to use the have_received matcher.
How can I get this test to pass?
Create a mock for Redis and stub out the class and instance methods — new and publish, respectively.
it "broadcasts creation" do
redis = stub_redis
Message.create(body: "foo")
expect(redis).to have_received(:publish).twice
end
def stub_redis
mock("redis").tap do |redis|
redis.stubs(:publish)
Redis.stubs(:new).returns(redis)
end
end
You could try using the expect_any_instance_of mock.
it 'broadcasts creation' do
expect(Redis.any_instance).to receive(:publish).twice
message = Message.create(body: 'foo')
end
https://www.relishapp.com/rspec/rspec-mocks/v/3-2/docs/working-with-legacy-code/any-instance

expecting a hash rather than an instance

I have this error
ArgumentError (wrong number of arguments (1 for 0)):
lib/law/production.rb:20:in `clone'
lib/law/production.rb:20:in `clone_law'
lib/law/production.rb:11:in `initialize'
app/controllers/laws_controller.rb:86:in `new'
app/controllers/laws_controller.rb:86:in `prod_law'
app/controllers/laws_controller.rb:44:in `create'
when using this
module Law
class Production
attr_accessor :law
attr_accessor :creator
def initialize(law,current_user)
#law = law
#creator = current_user
clone_law
end
def current__user
User.find_by_authentication_token(session[:_csrf_token])
end
def clone_law
clone(#law)
end
end
end
where clone, create, prod_law are some methods
I assume Rails is expecting a hash but I don't understand why
Firstly, clone is a standard Ruby method.
Secondly, it expects no arguments at
all (as error message says), it should be called on the object you want to clone, like this:
#law.clone

Delayed Job inside model - Wrong number of arguments

Here is my model:
class User
def join(race)
#blah blah blah ....
UserMailer.delay.join_race(self, race) #I'm stuck here
end
end
And my UserMailer like this
class UserMailer
def join_race(user, race)
#Another blah blah blah, nothing important here
mail(:to => user.email)
end
end
Now, whenever I call user.join(race), it shows the error like this:
ArgumentError: wrong number of arguments (2 for 1)
from /home/xxx/.rvm/gems/ruby-1.9.3-p194/gems/arel-3.0.2/lib/arel/expressions.rb:3:in `count'
from /home/xxx/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:224:in `binary?'
from /home/xxx/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:233:in `visit_String'
from /home/xxx/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:102:in `accept'
from /home/xxx/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:292:in `block in visit_Hash'
from /home/xxx/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:290:in `each'
...
If I convert it to normal function (without the .delay in front of join_race), it works.
I found similar issues but they are all about not calling .all method after using where. I suspect that the self could be the problem but I don't know how to make it work. If any of you have any clue, please share with me.
This issue is discussed in https://github.com/rails/arel/issues/149, including a mention of Mailer and delayed jobs, and the related https://github.com/rails/rails/issues/9263.
The following workaround was included:
module Arel
module Nodes
class SqlLiteral < String
def encode_with(coder)
coder['string'] = to_s
end
def init_with(coder)
clear << coder['string']
end
end
end
end
The problem is because I am using Rails 3.2.12. It seems like this problem only occurs in this version of Rails. Therefore, I upgrade my project to Rails 3.2.13 and everything is working properly.

Resources