Factory Girl and Rails with Cucumber gives undefined method error - ruby

I have a Model that looks like this
class Course < ActiveRecord::Base
attr_accessible :name
end
And my factories.rb looks like this
FactoryGirl.define do
factory :course do
name "testname"
end
end
Then when I call FactoryGirl.create(:course) in Cucumber like so:
Given /^there are courses in the database$/ do
FactoryGirl.create(:course)
end
I receive an undefined method error:
undefined method `name=' for #<Course id: nil, created_at: nil, updated_at: nil> (NoMethodError)
It all works fine when I use attr_accessor instead of attr_accessible in the Model, but according to other examples I've found it should work with both. Is this a bug or am I doing something wrong?
Links to examples where they say it should work:
How to create factories with attr_accessible?
https://groups.google.com/forum/#!topic/factory_girl/gjLXp96Acyg
https://gist.github.com/334413/2a0f60a9afbff321d3e96727ec17bab53c484128

Either should work provided that the fields in question exist in your database. ActiveRecord generates accessors (which FactoryGirl depends on) for attributes specified in attr_accessible but only if they are defined in the associated database table.

Related

Sinatra, ActiveRecord, FactoryGirl, and has_many_through associations

I am building a Sinatra API. My models use ActiveRecord and have a many-to-many model relationship.
class Workout < ActiveRecord::Base
has_many :workouts_exercises, dependent: :destroy
has_many :exercises, through: :workouts_exercises
end
class Exercise < ActiveRecord::Base
has_many :workouts_exercises
has_many :workouts, through: :workouts_exercises
end
class WorkoutsExercises < ActiveRecord::Base
belongs_to :workouts
belongs_to :exercises
end
I am trying to set up FactoryGirl to use these associations. Here is what I've got from reading all the docs I have found so far.
FactoryGirl.define do
factory :workout do
name 'Default Workout Factory Name'
description 'Default Workout Factory Description'
factory :workout_with_exercises do
after(:create) do |workout|
workout.exercises << FactoryGirl.create(:exercise)
end
end
end
end
FactoryGirl.define do
factory :exercise do
name 'Default Exercise Factory Name'
description 'Default Exercise Factory Description'
end
end
FactoryGirl.define do
factory :workouts_exercises do
workout
exercise
end
end
Here is my test that I would like to run
it 'returns the associated exercises' do
workout = FactoryGirl.create(:workout_with_exercises)
associated_exercises = workout.exercises
expect(associated_exercises.count).to eq(1)
end
However, when I run the specs I receive
1) Workout returns the associated exercises
Failure/Error: workout.exercises << FactoryGirl.create(:exercise)
NameError:
uninitialized constant Workout::WorkoutsExercise
The first method of debugging I tried was to pry before workout= declaration. Workout doesn't know about the exercises attribute. According to the ActiveRecord documentation, setting up a many-to-many association should provide you with the 16 #collection methods. This would mean that #exercises should return all the associated Exercise objects for the Workout object.
I can't, for the life of me, figure out whether or not it's my AR associations in the models that are to blame - or the FactoryGirl configuration I have. I've checked the SQL tables and all of the columns seem to be appropriate. I could really use some help figuring out what the issue is.
I've tried a few other FactoryGirl after_create hooks, using the shovel operator and attempting to declare the workouts_exercises association explicitly:
workout.exercises << [
create(:exercise, name: 'Bench Press', workouts_exercises: workout),
create(:exercise, name: 'Pushups', workouts_exercises: workout),
create(:exercise, name: 'DB Flys', workouts_exercises: workout)
]
Again, failure.
My third attempt was to use the FactoryGirl callback methods from ThoughtBot - ain't no calla back girl.
factory :workout_with_exercises do
after(:create) do |workout|
FactoryGirl.create(:exercise, workout: workout)
end
end
This results in
undefined method `exercise=' for #<Workout:0x007ff6250c2768>
Which makes me believe the AR associations aren't written correctly.
Thanks in advance for any help you can lend!
I use Sinatra, ActiveRecord, and RSpec. Just recently, I added FactoryGirl to the mix.
In your test framework's initialization point (I use RSpec, which is spec_helper.rb), you'll need to require the file that contains your model classes.
Next, do your model classes live within a module?
My model classes don't exist at the top-level, because I instead put them in a module for better organization within the project.
With this pattern, I found that I needed to explicitly define the module + class path for FactoryGirl.
The following would not work:
FactoryGirl.define do
factory :vendor do
name 'Test Vendor
end
end
But things began to work after I told FactoryGirl the full class name, like below:
FactoryGirl.define do
factory :vendor, class: MyAppName::Models::Vendor do
name 'Test Vendor
end
end

nil value reaching ActiveRecord validation despite correct value passed to constructor

Validation on a model object is failing despite what I think is the correct parameter value being whitelisted in the controller and passed to the model's constructor. What am I doing wrong?
OuterModel has_one Location via locatable. The latter is created using accepts_nested_attributes_for and validates only the :country attribute:
(EDIT: I found the error, it was hidden by code that I initially left out of the code here for simplification. See my answer below)
class OuterModel < Parent
has_one :location, as: locatable
accepts_nested_attributes_for :location
end
class Parent < ActiveRecord::Base
after_create :create_location
end
class Location < ActiveRecord::Base
belongs_to :locatable, polymorphic: true
validates :country, inclusion: {in: ["US", "CA"]}
end
Controller:
class OuterModelsController < ApplicationController
def create
#outer = OuterModel.new(outer_params)
if #outer.save
byebug #debug here
redirect_to outer_path(#outer)
end
end
def outer_params
params.require(:outer).permit(:name, :type,
location_attributes: [:country, :state, :city])
end
end
Using byebug I see the #outer.save call is satisfied, but the nested location object is not persisted because of a validation error:
(byebug) #outer.persisted? #true
(byebug) #outer.location.persisted? #false
(byebug) #outer.location.valid? #false
(byebug) #outer.location.country #nil
(byebug) #outer.id #6372
(byebug) #outer.location.errors
<ActiveModel::Errors:0x007f1f33c1eae0
#base=#<Location id: nil, city: nil, country: nil, state: nil, locatable_id: 6732, locatable_type: "OuterModel", created_at: nil, updated_at: nil>,
#messages={:country=>["is not included in the list"]}>
However, the controller outer_params method appears to be sending the correct hash to OuterModel.new:
{"name"=>"A name",
"type"=>"OuterModel",
"location_attributes"=>{
"city"=>"South Deionview", "country"=>"US", "state"=>"IL"
}
}
Why then is the Location.country value nil after the call to save, and why is validation failing?
EDIT: logfile (much simplified) pastebin here. It seems like the correct values are being sent as part of the SQL
The answer lay in a parent of OuterModel that had an after_create hook to create a Location. Added the parent class to the source above.
The Location object was initially created correctly by accepts_nested_attributes_for (as per all evidence from logging) but then the after_create hook replaced it with an empty Location object that failed validation.

Confusion about ways to use JSON in ruby sinatra application

I'm making a Ruby Sinatra application that uses mongomapper and most of my responses will be in the JSON form.
Confusion
Now I've come across a number of different things that have to do with JSON.
The Std-lib 1.9.3 JSON class: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/json/rdoc/JSON.html
The JSON Gem: http://flori.github.io/json/
ActiveSupport JSON http://api.rubyonrails.org/classes/ActiveSupport/JSON.html because I'm using MongoMapper which uses ActiveSupport.
What works
I'm using a single method to handle responses:
def handleResponse(data, haml_path, haml_locals)
case true
when request.accept.include?("application/json") #JSON requested
return data.to_json
when request.accept.include?("text/html") #HTML requested
return haml(haml_path.to_sym, :locals => haml_locals, :layout => !request.xhr?)
else # Unknown/unsupported type requested
return 406 # Not acceptable
end
end
the line:
return data.to_json
works when data is an instance of one of my MongoMapper model classes:
class DeviceType
include MongoMapper::Document
plugin MongoMapper::Plugins::IdentityMap
connection Mongo::Connection.new($_DB_SERVER_CNC)
set_database_name $_DB_NAME
key :name, String, :required => true, :unique => true
timestamps!
end
I suspect in this case the to_json method comes somewhere from ActiveSupport and is further implemented in the mongomapper framework.
What doesn't work
I'm using the same method to handle errors too. The error class I'm using is one of my own:
# Superclass for all CRUD errors on a specific entity.
class EntityCrudError < StandardError
attr_reader :action # :create, :update, :read or :delete
attr_reader :model # Model class
attr_reader :entity # Entity on which the error occured, or an ID for which no entity was found.
def initialize(action, model, entity = nil)
#action = action
#model = model
#entity = entity
end
end
Of course, when calling to_json on an instance of this class, it doesn't work. Not in a way that makes perfect sense: apparantly this method is actually defined. I've no clue where it would come from. From the stack trace, apparently it is activesupport:
Unexpected error while processing request: object references itself
object references itself
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:75:in `check_for_circular_references'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:46:in `encode'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `block in encode_json'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `each'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `map'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `encode_json'
But where is this method actually defined in my class?
The question
I will need to override the method in my class like this:
# Superclass for all CRUD errors on a specific entity.
class EntityCrudError < StandardError
def to_json
#fields to json
end
end
But I don't know how to proceed. Given the 3 ways mentioned at the top, what's the best option for me?
As it turned out, I didn't need to do anything special.
I had not suspected this soon enough, but the problem is this:
class EntityCrudError < StandardError
..
attr_reader :model # Model class
..
end
This field contains the effective model class:
class DeviceType
..
..
end
And this let to circular references. I now replaced this with just the class name, which will do for my purposes. Now to_json doesn't complain anymore and I'm happy too :)
I'm still wondering what's the difference between all these JSON implementations though.

Ruby - undefined method `empty?' for nil:NilClass

More than 3 hours I am trying to solve pretty easy error (on the first glance):
undefined method `empty?' for nil:NilClass
but still no success.
I have the DB table products, which contains the columns category_id and manufacturer_id.
Associations:
class Product < ActiveRecord::Base
belongs_to :manufacturer
belongs_to :category
...
end
class Category < ActiveRecord::Base # the same for Manufacturer
has_ancestry
has_many :products
end
Trying to grab some data:
Product.where('category_id IS NOT NULL AND manufacturer_id IS NOT NULL').each do |product|
...
puts product.manufacturer.name # here's the error
puts product.category.name # here's the error
...
end
I fetched all rows, where is not NIL value in the columns manufacturer_id and category_id... so how can I get this error?
Also, I've tried:
...
puts product.manufacturer.name unless product.manufacturer_id.nil?
puts product.category.name unless product.category_id.nil?
...
What am I doing wrong?
You most likely deleted a manufacturer or category so there's no corresponding record to match the foreign key.
Problably is a business logic problem on your database considering your query.
IMHO u are calling some objects that will not be valid ones. Check your database to see if all registers attend your validations logic.

Rails 3 scope only select certain attributes for a has_many relationship

This looks like it should be something pretty easy but I can't seem to get it to work. I have a model with a has_many relationship and I'd like a scope on the parent that allows me to select only certain attributes for each.
An example:
class Bakery < ActiveRecord::Base
has_many :pastries
scope :summary, select([:id, :name, 'some option calling pastries.summary'])
class Pastry < ActiveRecord::Base
belongs_to :bakery
scope :summary, select([:id, :image_url])
I'd like to be able to call something like Bakery.first.summary and get a Bakery model with only the id and name populated and for each pastry in it's pastries array to only have the id and image_url attributes populated.
You could do this, but it won't affect the SQL queries that are made as a result (assuming you're trying to optimise the underlying query?):
class Pastry
...
def summary
{
:id => self.id,
:image_url => self.image_url
}
end
end
class Bakery
...
def summary
pastries.collect {|i| i.summary }
end
end
This would then give you an array of hashes, not model instances.
ActiveRecord doesn't behave how you're expecting with models - it will fetch whatever data it thinks you need. You could look at using the Sequel gem instead, or executing a raw SQL query such as:
Pastry.find_by_sql("SELECT id, name from ...")
But this could give you unexpected behaviour.

Resources