I'm having this issue with factory girl where it gives me a undefined method 'each' for #<String:0x0000012915bc18> error with a serialized field coming from the factory.
within ActiveRecord, it runs the each with no problem, as the object returned is an array.
My question is: how should I format the serialized object in my factory? The way that active record returns it? or the way it's actually stored in the database? (i.e. serialized or not?) will rspec do the same serialize magic on saving and retrieving that active record does?
this is a simplified version of what I'm doing:
Tvdb.rb-- Model
class Tvdb < ActiveRecord::Base
set_table_name 'tvdb'
serialize :cache
def self.episodes(id)
cached = self.find_by_term('episodes_' + id.to_s)
return cached.cache unless cached.nil?
info = self.series_info(id)
request = info.episodes
Tvdb.create(:term=>'episodes_' + info.id.to_s, :cache=>request)
return request
end
end
Then in my Series.rb model I can do this:
class Series < ActiveRecord::Base
def episodes
episodes = Tvdb.episodes(self.tvdb_id)
episodes.each do |episode|
puts episode.name
end
end
end
Tvdb.rb -- Factory
FactoryGirl.define do
factory :series1_episodes, :class=>Tvdb do
term 'episodes_79488'
cache %q([#<AnObject::Module:0x000001290a4568 #value="dsada"]>,#<AnObject::Module:0x0002321290a4568 #value="dsadsada"]> )
end
end
note: The syntax of the cache value might be invalid here, I tried to shorten what was a very long serialized object. The point is that it works in my model, but not in rspec
and in my *series_spec.rb* calling this:
series.episodes.count.should_not == 0
gives that error
undefined method 'each' for #<String:0x0000012915bc18>
In your factory, you shouldn't set cache to the serialized value, but to the actual value.
FactoryGirl.define do
factory :series1_episodes, :class => Tvdb do
term 'episodes_79488'
cache ["foo", "bar"]
end
end
You can change it to JSON this way:
FactoryGirl.define do
factory :series1_episodes, class: Tvdb do
term { 'episodes_79488' }
cache { %i[dsada dsadsada].to_json }
end
end
Related
I have an ActiveRecord model instance where I want to modify attributes where some attributes exist on an association rather than directly on the model itself. All of the attributes come in as a hash.
This code is part of my test suite so I'm not concerned about issues of mass attribute assignment in this case.
If I try to update using instance.update_attributes(hash) this fails.
As an alternative I tried looping over the hash and setting the attributes like this.
hash.each do |key, val|
instance[key] = val
end
This method will set attributes that exist directly on the instance, but throws an exception on attributes that are tied to associated records ActiveModel::MissingAttributeError Exception: can't write unknown attribute 'foo'
Give the delegate approach a go. Something along the lines of:
class Foo
attr_accessor :bar, :foo_attribute
delegate :bar_attribute=, to: :bar
def initialize
#bar = Bar.new
end
end
class Bar
attr_accessor :bar_attribute
end
Then, in the console:
hsh = {foo_attribute: 'foo', bar_attribute: 'bar'}
f = Foo.new
hsh.each do |k,v|
f.send("#{k}=",v)
end
f.inspect
=> "#<Foo:0x00000004f1f0f0 #bar=#<Bar:0x00000004f1f0c8 #bar_attribute=\"bar\">, #foo_attribute=\"foo\">"
f.bar.inspect
=> "#<Bar:0x00000004f1f0c8 #bar_attribute=\"bar\">"
I want a simple way to test my validations. My test focus are integrations, not validations, and I don't working with TDD. That being said, I want to replace:
# Estate model specs
describe "#name" do
it "is required" do
estate.name = nil
estate.valid?
expect(estate.errors).to have_key(:name)
end
end
describe "#company" do
it "is required" do
estate.company = nil
estate.valid?
expect(estate.errors).to have_key(:company)
end
end
# and so on..
to some like:
# Estate model specs
requiredFields = [:name, :company, :price, :region, :regions, :typologies, :image]
requiredFields.each do |requiredField|
describe "##{requiredField}" do
it "is required" do
estate[requiredField] = nil
estate.valid?
expect(estate.errors).to have_key(requiredField)
end
end
end
The fields name and price works, the problem is in associations:
Estate
is an instance of Estate
validations
with required fields
should be valid
#name
is required
#company
is required (FAILED - 1)
#price
is required
#region
is required (FAILED - 2)
...
I think that problem is estate[requiredField]. If I change to company_id will work. How can I do something like estate.requiredField in foreach?
You can perform estate.requiredField using Ruby's send method, which invokes its argument as a method on the receiver:
estate.send(requiredField)
Since you're assigning, you'll need to interpolate an = into the field name. The foo= method takes an argument, which you pass as continued arguments to send after the method name:
estate.send("#{requiredField}=", nil)`
Something to be aware of is that since send ends up calling the requested method from within the receiver, you might end up bypassing protected or private methods.
class Dog
private
def bark
puts 'Woof'
end
end
Dog.new.bark
# NoMethodError: private method 'bark' called for #<Dog:0x007fd28bad7590>
Dog.new.send :bark
# Woof
Try to use object.send
estate.send("#{requiredField}=", value) # setter, estate.requiredField=value
estate.send(requiredField) # getter, estate.requiredField
Or use https://github.com/thoughtbot/shoulda-matchers for such kind of tests
I'm performing the simplest test on the following class (inside model's folder):
class Offer
attr_accessor :title, :payout, :thumbnail
def initialize(title, payout, thumbnail)
#title = title
#payout = payout
#thumbnail = thumbnail
end
end
The thing is there's no 'offers' db table. The objects created out of this class are never saved in a database.
Then i perform the tests using rspec:
describe Offer do
it "has a valid factory" do
expect(FactoryGirl.create(:offer)).to be_valid
end
...
end
and FactoryGirl:
FactoryGirl.define do
factory :offer do
skip_create
title { Faker::Name.name }
payout { Faker::Number.number(2) }
thumbnail { Faker::Internet.url }
initialize_with { new(title, payout, thumbnail)}
end
end
And i get the following error:
> undefined method `valid?' for #<Offer:0x00000002b78958>
Because your Offer class is not inheriting from ActiveRecord::Base, you're not getting any of the stuff that comes along with it (such as validations). valid? is a method provided through ActiveRecord's modules, not by Ruby directly, so it won't be available on a basic Ruby class.
If all you care about is validations, then you can include the ActiveModel::Validations module in your class and it will give you valid? as well as validates_presence_of, etc.:
class Offer
include ActiveModel::Validations
...
end
You can also just include ActiveModel to get a couple other things such as ActiveRecord's naming and conversion benefits (as well as validation).
I have been looking for over an hour on Internet and I can't find anything about this.
I am creating filters for data of a website and currently these are handled by a case statement
class MyClass
attr_accessor :attribute
def self.function(value)
query = case value
when "open" then "Open"
...
end
where(:attribute => query)
end
end
Because of various reasons (i.e. dynamic instead of hard coding the filters) I want to create a model out of this with a getter and setter, but I can't get this to work
My new function:
def self.function(value)
Attribute.name = value
where(:attribute => Attribute.name)
end
My new model:
class Attribute
attr_accessor :name
end
And the test:
it "should set the attribute to 'hello'" do
MyClass.function("hello")
Attribute.name.should eql "hello"
end
gives an error:
Failure/Error: Myclass.function("hallo")
NoMethodError:
undefined method `name=' for Attribute:Class
Any help would be appreciated
This is because the attr_accessor is defining instance method (ie: method that works on an instance of Attribute) and you try to use it as class method (ie: Attribute.name).
You may rewrite your function this way :
def self.function(value)
attribute = Attribute.new
attribute.name = value
where(:attribute => attribute.name)
end
While trying to add an error message using add_to_base, I am getting an undefined method 'errors' message. I am defining it in my model. Am I supposed to include any other file in order to access the errors variable.
Model File - I am defining it inside a method
self.errors.add_to_base("Invalid Name")
Error Message
undefined method `errors' for #<Class:0x0000010179d7a0>
I tried by calling it as errors.add_to_base("Invalid Name") also but still getting the same error.
Thanks.
you should call it in your callback method, something like following
def validate
if !self.interests.blank? && !self.interests.match("<").nil?
self.errors.add :base, 'Please ensure that Interest field do not contain HTML(< and >) tags'
end
end
I suspect that you have defined your method as a class method, instead of as an instance method.
Class methods look like this on ruby:
def self.checkFoo()
...
end
Instance methods looks like this:
def checkFoo()
...
end
Check that your checkFoo method is an instance method, and then use it like this:
class MyModel < ActiveRecord::Base
validate :foo
private
def checkFoo()
self.errors.add etc..
end
end
Typically used the validation callbacks, model errors are used both to cause the prospective database save to fail and to set up a contextual error messages for the end-user. The add_to_base variant is intended for general, non-specific error conditions (i.e. not associated with a particular model attribute).
class MyModel < ActiveRecord::Base
validate do |my_model|
if my_model.some_attribute.blank? # For example
my_model.errors.add :my_model, "must be filled in"
end
end
end
Subsequently
#my_model = MyModel.create(:some_attribute => "")
would fail and the #my_model.errors.full_messages array would contain
[ ..., "Some_attribute must be filled in", ... ]
There is however a shorthand for the above example as follows
class MyModel < ActiveRecord::Base
validates_presence_of :some_attribute, :msg => "must be filled in"
end
Looks like your 'self.errors.add_to_base("Invalid Name")' doesn't have any problem
But your model should inherit from ActiveRecord::Base
cheers
sameera