Why is my define_method not working? - ruby-on-rails

I have two methods in my Designer class (in my rails app):
def add_specialty(specialty)
specialty_list.add(specialty)
save
end
def add_qualification(qualification)
qualification_list.add(qualification)
save
end
Here are specs I have for them that are passing:
context 'adding specialties' do
it "can add a new specialty" do
expect { designer.add_specialty("interior design") }.to change {designer.specialty_list.count}.by(1)
expect(designer.specialty_list).to include("interior design")
end
end
context 'adding qualifications' do
it "can add a new qualification" do
expect { designer.add_qualification("architect") }.to change {designer.qualification_list.count}.by(1)
expect(designer.qualification_list).to include("architect")
end
end
Now I want to refactor to this implementation:
["specialty", "qualification"].each do |attr|
define_method("add_#{attr}") do |arg|
"#{attr}_list".add(arg)
save
end
end
This fails. I get failures:
1) Designer adding qualifications can add a new qualification
Failure/Error: expect { designer.add_qualification("architect") }.to change {designer.qualification_list.count}.by(1)
NoMethodError:
undefined method `add' for "qualification_list":String
# ./app/models/designer.rb:93:in `block (2 levels) in <class:Designer>'
# ./spec/models/designer_spec.rb:79:in `block (4 levels) in <top (required)>'
# ./spec/models/designer_spec.rb:79:in `block (3 levels) in <top (required)>'
# -e:1:in `<main>'
2) Designer adding specialties can add a new specialty
Failure/Error: expect { designer.add_specialty("interior design") }.to change {designer.specialty_list.count}.by(1)
NoMethodError:
undefined method `add' for "specialty_list":String
# ./app/models/designer.rb:93:in `block (2 levels) in <class:Designer>'
# ./spec/models/designer_spec.rb:72:in `block (4 levels) in <top (required)>'
# ./spec/models/designer_spec.rb:72:in `block (3 levels) in <top (required)>'
# -e:1:in `<main>'
What am I doing wrong in my define_method implementation?

"#{attr}_list" by itself is just the string "specialty_list" or "qualification_list", and strings don't have an add method. I think you want to the send the specialty_list method e.g.
%w{ specialty qualification }.each do |attr|
define_method("add_#{attr}") do |arg|
send("#{attr}_list").add(arg)
save
end
end

Ok: I got it working like this:
["specialty", "qualification"].each do |attr|
define_method("add_#{attr}") do |arg|
instance_eval("#{attr}_list").send(:add, arg)
save
end
end
Not sure why this worked though or if its the right way to do it. Would anyone care to contribute towards a better understanding?

Related

RSpec respond_to does not work

This is my code snippet to test the polymorphic relationship.
I wrote test as below.
require 'rails_helper'
RSpec.describe Proration, type: :model do
let(:invoice_item) { FactoryGirl.create(:proration_invoice_item) }
subject { :invoice_item }
it { should be_valid }
it { should respond_to(:total_amount) }
it { should respond_to(:invoice) }
it { should respond_to(:itemable) }
end
But this errors out
Failures:
1) Proration should be valid
Failure/Error: it { should be_valid }
NoMethodError:
undefined method `valid?' for :invoice_item:Symbol
# ./spec/models/proration_spec.rb:7:in `block (2 levels) in <top (required)>'
2) Proration Proration should respond to #total_amount
Failure/Error: end
expected :invoice_item to respond to :total_amount
# ./spec/models/proration_spec.rb:16:in `block (3 levels) in <top (required)>'
3) Proration Proration should respond to #invoice
Failure/Error: end
expected :invoice_item to respond to :invoice
# ./spec/models/proration_spec.rb:17:in `block (3 levels) in <top (required)>'
4) Proration Proration should respond to #itemable
Failure/Error: Unable to find matching line in /Users/toshikiinami/Desktop/billing/spec/models/proration_spec.rb
expected :invoice_item to respond to :itemable
# ./spec/models/proration_spec.rb:18:in `block (3 levels) in <top (required)>'
Any ideas to fix this?
(respond_to for model doesn't work.)
You defined an invoice_item using let, but as a subject you're using symbol. What you want instead is that object invoice_item, so
subject{ :invoice_item }
should be changed to
subject{ invoice_item }

How to include view helper in RSpec

I have a module with a custom view helper. Actually it uses the modified code of link_to helper. Just append a query string to the generated link. I have existing tests which fails because the custom helper cannot be found. How can I include it so it is available for RSpec? I tried the following approaches:
1.Using include:
describe MyClass do
include MyHelper
2.Using configure
RSpec.configure do |config|
config.include MyHelper
end
3.Adding it in spec_helper.rb
config.include MyHelper
Here is the error:
1) Mailer#calendar_item_notification should send email for task
Failure/Error: expect {Mailer.calendar_item_notification('User', #user.id).deliver}.to change { ActionMailer::Base.deliveries.count }.by(1)
ActionView::Template::Error:
undefined method `<<' for #<Proc:0x00000013102d28>
# ./app/helpers/mailer_helper.rb:88:in `link_username'
# ./app/views/user_mailer/calendar_item_notification.html.erb:137:in `_app_views_user_mailer_calendar_item_notification_html_erb___206027131771876632_159436760'
# ./app/mailers/user_mailer.rb:136:in `calendar_item_notification'
# ./spec/mailers/user_mailer_spec.rb:19:in `block (4 levels) in <top (required)>'
# ./spec/mailers/user_mailer_spec.rb:19:in `block (3 levels) in <top (required)>'
# ./spec/support/misc.rb:13:in `block in suppress_output'
# ./spec/support/misc.rb:12:in `tap'
# ./spec/support/misc.rb:12:in `suppress_output'
# -e:1:in `<main>'
Thanks

NoMethodError: undefined method `addrs' for #<Mail::UnstructuredField:0x007fc5c680bf18>

So I was testing emails with RSpec and the email_spec gem and I keep coming across this error that leaves me with no real clue as to what is causing it.
I am very new to RSpec and testing in general. When I googled this the error I found one post that was similar to this error as well as a brief documentation page on the error (which came off to me as very ambiguous).
I believe the reason is due to a possibly outdated gem or method from email_spec, but I am just speculating (there's a pun for your time).
This is the error I keep receiving (I redacted my email for this post):
1) UserMailer it sends an activation email when the user signs up should be delivered from XXXXXXXX#gmail.com
Failure/Error: it { should deliver_from "XXXXXXXX#gmail.com" }
NoMethodError:
undefined method `addrs' for #<Mail::UnstructuredField:0x007fc5c680bf18>
# ./spec/mailers/user_mailer_spec.rb:12:in `block (3 levels) in <top (required)>'
Few things to point out:
I am not directly referencing the addrs method in any of my tests (so I believe)
I have included Email::Helpers and Email::Matchers in my config/test.rb file.
Here is my UserMailer test:
require "spec_helper"
require "email_spec"
describe UserMailer do
describe "it sends an activation email when the user signs up" do
user = FactoryGirl.build(:user)
subject { UserMailer.activate_account(user) }
it { should deliver_to user.email }
# Error seems to be occurring from the "deliver_from" method
it { should deliver_from "XXXXXXX2#gmail.com" }
it { should have_subject "Please confirm to activate your account." }
it { should have_body_text "Hi, #{user.first_name}" }
it { should have_body_text "Confirm Your Account" }
end
end
I would just like to be helped and pointed in the right direction as to what is causing this issue so I may take the appropriate steps to resolve it.
EDIT
Added the backtrace as requested.
# /Library/Ruby/Gems/2.0.0/gems/mail-2.6.3/lib/mail/field.rb:189:in `method_missing'
# /Library/Ruby/Gems/2.0.0/gems/email_spec-1.6.0/lib/email_spec/matchers.rb:92:in `block in matches?'
# /Library/Ruby/Gems/2.0.0/gems/email_spec-1.6.0/lib/email_spec/matchers.rb:6:in `address_array'
# /Library/Ruby/Gems/2.0.0/gems/email_spec-1.6.0/lib/email_spec/matchers.rb:92:in `matches?'
# /Library/Ruby/Gems/2.0.0/gems/rspec-expectations-2.99.2/lib/rspec/expectations/handler.rb:24:in `handle_matcher'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/memoized_helpers.rb:77:in `should'
# ./spec/mailers/user_mailer_spec.rb:12:in `block (3 levels) in <top (required)>'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/extensions/instance_eval_with_args.rb:16:in `instance_exec'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/extensions/instance_eval_with_args.rb:16:in `instance_eval_with_args'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example.rb:116:in `block in run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example.rb:178:in `call'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example.rb:178:in `run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/extensions/instance_eval_with_args.rb:16:in `instance_exec'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/extensions/instance_eval_with_args.rb:16:in `instance_eval_with_args'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example.rb:241:in `instance_eval_with_args'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/hooks.rb:106:in `block (2 levels) in run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/hooks.rb:108:in `call'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/hooks.rb:108:in `run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/hooks.rb:446:in `run_hook'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example_group.rb:463:in `run_around_each_hooks'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example.rb:250:in `with_around_each_hooks'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example.rb:113:in `run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example_group.rb:515:in `block in run_examples'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example_group.rb:511:in `map'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example_group.rb:511:in `run_examples'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example_group.rb:496:in `run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example_group.rb:497:in `block in run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example_group.rb:497:in `map'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/example_group.rb:497:in `run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/command_line.rb:24:in `block (2 levels) in run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/command_line.rb:24:in `map'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/command_line.rb:24:in `block in run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/reporter.rb:58:in `report'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/command_line.rb:21:in `run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/runner.rb:103:in `run'
# /Library/Ruby/Gems/2.0.0/gems/rspec-core-2.99.2/lib/rspec/core/runner.rb:17:in `block in autorun'
Excellent! I was able to figure it out.
Issues was in my actual UserMailer class (in /app/mailers/user_mailer.rb).
The default from: statement at the top did not match with my tests.
class UserMailer < ApplicationMailer
default from: "XXXXXXXX2#gmail.com" # <= This was the culprit
def activate_account(user)
#user = user
mail(:to => #user.email, :subject => "Please confirm to activate your account.")
end
end
I recently ran into this and my problem was that i hadn't provided a proper email address in the :to field
By mistake I had -
mail(to: #user)
Instead of
mail(to: #user.email)

Rake db:seed NoMethodError: undefined method `sample'

I am trying to seed my database, but I am running into this error, and as a beginner do not know how to fix it. I'm trying to take a sample of bookmarks and assign them to some of my users. Any ideas why this is throwing an error? Here is the error:
vagrant#rails-dev-box:~/code/bookmarks$ rake db:seed
rake aborted!
NoMethodError: undefined method `sample' for #<Class:0xaa90cf4>
/home/vagrant/.rvm/gems/ruby-2.0.0-p481/gems/activerecord-4.0.5/lib/active_record/dynamic_matchers.rb:22:in `method_missing'
/home/vagrant/code/bookmarks/db/seeds.rb:47:in `block (2 levels) in <top (required)>'
/home/vagrant/code/bookmarks/db/seeds.rb:46:in `times'
/home/vagrant/code/bookmarks/db/seeds.rb:46:in `block in <top (required)>'
/home/vagrant/.rvm/gems/ruby-2.0.0-p481/gems/activerecord-4.0.5/lib/active_record/relation/delegation.rb:13:in `each'
/home/vagrant/.rvm/gems/ruby-2.0.0-p481/gems/activerecord-4.0.5/lib/active_record/relation/delegation.rb:13:in `each'
/home/vagrant/code/bookmarks/db/seeds.rb:45:in `<top (required)>'
/home/vagrant/.rvm/gems/ruby-2.0.0-p481/gems/activesupport-4.0.5/lib/active_support/dependencies.rb:223:in `load'
/home/vagrant/.rvm/gems/ruby-2.0.0-p481/gems/activesupport-4.0.5/lib/active_support/dependencies.rb:223:in `block in load'
/home/vagrant/.rvm/gems/ruby-2.0.0-p481/gems/activesupport-4.0.5/lib/active_support/dependencies.rb:214:in `load_dependency'
/home/vagrant/.rvm/gems/ruby-2.0.0-p481/gems/activesupport-4.0.5/lib/active_support/dependencies.rb:223:in `load'
/home/vagrant/.rvm/gems/ruby-2.0.0-p481/gems/railties-4.0.5/lib/rails/engine.rb:540:in `load_seed'
/home/vagrant/.rvm/gems/ruby-2.0.0-p481/gems/activerecord-4.0.5/lib/active_record/tasks/database_tasks.rb:154:in `load_seed'
/home/vagrant/.rvm/gems/ruby-2.0.0-p481/gems/activerecord-4.0.5/lib/active_record/railties/databases.rake:181:in `block (2 levels) in <top (required)>'
Tasks: TOP => db:seed
Edit: Here is my seeds.rb file:
require 'faker'
# Create a User
# user = User.new(
# name: 'First Last',
# email: 'firstlast#gmail.com',
# password: 'password',
# )
# user.skip_confirmation!
# user.save
#Create Users
5.times do
user = User.new(
name: Faker::Name.name,
email: Faker::Internet.email,
password: Faker::Lorem.characters(10)
)
user.skip_confirmation!
user.save!
end
users = User.all
#Create Bookmarks
10.times do
Bookmark.create!(
url: Faker::Internet.url
)
end
bookmarks = Bookmark.all
#Create Topics
10.times do
Topic.create!(
name: Faker::Lorem.sentence
)
end
topics = Topic.all
users.each do |user|
3.times do
user.bookmarks << Bookmark.sample
end
end
topics.each do |topic|
3.times do
user.topics << Topic.sample
end
end
puts "Seed finished"
puts "#{User.count} users created"
puts "#{Bookmark.count} bookmarks created"
puts "#{Topic.count} topics created"
Thanks in advance for your help!
whereever you are calling .sample, the object before it is apparently not the correct type of object. It should either be an array or the 'collection' which results when you query your database. Like this...
posts = Post.all
posts.sample would work correctly
any object which is not an array (or like an array) will not have the 'sample' method available for you to use
You cannot call sample on a class as that is not a valid method. The sample method is used for arrays or ActiveRecord::Relation arrays.
For example, each of these would return a random
[0,1,2,3].sample
(0..10).to_a.sample
Post.all.sample
However, something like these examples would give an error.
User.sample
NoMethodError: undefined method 'sample' for #<Class:0x00000007faa378>
(9..199).sample
NoMethodError: undefined method 'sample' for 9..199:Range

Undefined method 'action' running single RSpec file for a rails controller

I have a peculiar situation - an rspec file fails when run independently, but run okay when run as part of the entire suite.
Failure/Error: visit oauth_callback_path
NoMethodError:
undefined method `action' for MyController:Class
# <internal:prelude>:10:in `synchronize'
# ./spec/requests/login_spec.rb:xx:in `block (5 levels) in <top (required)>'
# ./spec/requests/login_spec.rb:xx:in `block (4 levels) in <top (required)>'
Simplified spec:
require 'spec_helper'
class MyController
def oauth_response
sign_in(
ENV['TEST_ACCESS_TOKEN'],
ENV['TEST_ACCESS_SECRET'])
redirect_to root_path
end
end
describe 'logging in' do
it 'login' do
visit oauth_callback_path
response.should be_success
end
end
I believe the problem is that MyController is not extending ApplicationController. That's why the method action is not defined for MyController.
The class MyController appears to be blocking Rails magic class loading. Either the test should explicitly require the controller, or the extension should be defined with MyController.class_eval

Resources